builder.rs 6.64 KB
Newer Older
1
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
2
//
3
4
5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
10
11
12
13
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
14

Hero Bird's avatar
Hero Bird committed
15
16
use core::marker::PhantomData;

17
use crate::env::{
18
19
    call::{
        state,
20
21
22
23
24
        ArgsList,
        Argument,
        ArgumentList,
        EmptyArgumentList,
        ExecutionInput,
25
        Selector,
26
    },
27
    EnvTypes,
28
    Result,
29
30
31
32
33
34
35
36
37
};

/// Represents a return type.
///
/// Used as a marker type to differentiate at compile-time between invoke and evaluate.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ReturnType<T>(PhantomData<fn() -> T>);

/// The final parameters to the cross-contract call.
38
pub struct CallParams<E, Args, R>
39
40
41
42
43
44
45
46
where
    E: EnvTypes,
{
    /// The account ID of the to-be-called smart contract.
    callee: E::AccountId,
    /// The maximum gas costs allowed for the call.
    gas_limit: u64,
    /// The transferred value for the call.
47
    transferred_value: E::Balance,
48
49
50
    /// The expected return type.
    return_type: PhantomData<ReturnType<R>>,
    /// The already encoded call data respecting the ABI.
51
    call_data: ExecutionInput<Args>,
52
53
54
}

/// Builds up a call.
55
pub struct CallBuilder<E, Args, R, Seal>
56
57
58
59
where
    E: EnvTypes,
{
    /// The current parameters that have been built up so far.
60
    params: CallParams<E, Args, R>,
61
62
63
64
    /// Seal state.
    seal: PhantomData<Seal>,
}

65
impl<E, Args, R> CallParams<E, Args, R>
66
67
68
69
where
    E: EnvTypes,
{
    /// The code hash of the contract.
70
    #[inline]
71
72
73
74
75
    pub fn callee(&self) -> &E::AccountId {
        &self.callee
    }

    /// The gas limit for the contract instantiation.
76
    #[inline]
77
78
79
    pub fn gas_limit(&self) -> u64 {
        self.gas_limit
    }
80
    /// The transferred value for the called contract.
81
    #[inline]
82
83
    pub fn transferred_value(&self) -> &E::Balance {
        &self.transferred_value
84
85
86
    }

    /// The raw encoded input data.
87
88
    #[inline]
    pub fn input_data(&self) -> &ExecutionInput<Args> {
89
90
91
92
        &self.call_data
    }
}

93
impl<E, R> CallParams<E, EmptyArgumentList, R>
94
95
96
97
98
where
    E: EnvTypes,
    E::Balance: Default,
{
    /// Creates the default set of parameters for the cross-contract call.
99
    #[inline]
100
101
102
103
    fn new(callee: E::AccountId, selector: Selector) -> Self {
        Self {
            callee,
            gas_limit: 0,
104
            transferred_value: E::Balance::default(),
105
            return_type: PhantomData,
106
            call_data: ExecutionInput::new(selector),
107
108
109
110
        }
    }

    /// Returns a builder for a cross-contract call that might return data.
111
    #[inline]
112
113
114
    pub fn eval(
        callee: E::AccountId,
        selector: Selector,
115
    ) -> CallBuilder<E, EmptyArgumentList, ReturnType<R>, state::Unsealed> {
116
117
118
119
120
121
122
123
        CallBuilder {
            params: CallParams::new(callee, selector),
            seal: Default::default(),
        }
    }

    /// Returns a builder for a cross-contract call that cannot return data.
    ///
124
    /// Prefer this over [`CallParams::eval`] if possible since it is the more efficient operation.
125
    #[inline]
126
127
128
    pub fn invoke(
        callee: E::AccountId,
        selector: Selector,
129
    ) -> CallBuilder<E, EmptyArgumentList, (), state::Unsealed> {
130
131
132
133
134
135
136
        CallBuilder {
            params: CallParams::new(callee, selector),
            seal: Default::default(),
        }
    }
}

137
impl<E, Args, R, Seal> CallBuilder<E, Args, R, Seal>
138
139
140
141
where
    E: EnvTypes,
{
    /// Sets the maximumly allowed gas costs for the call.
142
    #[inline]
143
144
145
146
147
148
    pub fn gas_limit(mut self, gas_limit: u64) -> Self {
        self.params.gas_limit = gas_limit;
        self
    }

    /// Sets the value transferred upon the execution of the call.
149
    #[inline]
150
151
    pub fn transferred_value(mut self, value: E::Balance) -> Self {
        self.params.transferred_value = value;
152
153
154
155
        self
    }
}

156
impl<E, R> CallBuilder<E, EmptyArgumentList, R, state::Unsealed>
157
158
159
160
where
    E: EnvTypes,
{
    /// Pushes an argument to the inputs of the call.
161
162
163
164
165
    #[inline]
    pub fn push_arg<A>(
        self,
        arg: A,
    ) -> CallBuilder<E, ArgumentList<Argument<A>, EmptyArgumentList>, R, state::Unsealed>
166
167
168
    where
        A: scale::Encode,
    {
169
170
171
172
173
174
175
176
177
178
        CallBuilder {
            params: CallParams {
                call_data: self.params.call_data.push_arg(arg),
                callee: self.params.callee,
                gas_limit: self.params.gas_limit,
                transferred_value: self.params.transferred_value,
                return_type: self.params.return_type,
            },
            seal: Default::default(),
        }
179
    }
180
}
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
impl<'a, E, ArgsHead, ArgsRest, R>
    CallBuilder<E, ArgsList<ArgsHead, ArgsRest>, R, state::Unsealed>
where
    E: EnvTypes,
{
    /// Pushes an argument to the inputs of the call.
    #[inline]
    pub fn push_arg<A>(
        self,
        arg: A,
    ) -> CallBuilder<E, ArgsList<A, ArgsList<ArgsHead, ArgsRest>>, R, state::Unsealed>
    where
        A: scale::Encode,
    {
        CallBuilder {
            params: CallParams {
                call_data: self.params.call_data.push_arg(arg),
                callee: self.params.callee,
                gas_limit: self.params.gas_limit,
                transferred_value: self.params.transferred_value,
                return_type: self.params.return_type,
            },
            seal: Default::default(),
        }
    }
}

impl<E, Args, R> CallBuilder<E, Args, R, state::Unsealed>
where
    E: EnvTypes,
{
213
    /// Seals the call builder to prevent further arguments.
214
215
    #[inline]
    pub fn seal(self) -> CallBuilder<E, Args, R, state::Sealed> {
216
217
218
219
220
221
222
        CallBuilder {
            params: self.params,
            seal: Default::default(),
        }
    }
}

223
impl<E, Args, R, Seal> CallBuilder<E, Args, ReturnType<R>, Seal>
224
where
225
    E: EnvTypes,
226
    Args: scale::Encode,
227
228
229
230
    R: scale::Decode,
{
    /// Fires the call to the remote smart contract.
    /// Returns the returned data back to the caller.
231
    #[inline]
232
    pub fn fire(self) -> Result<R>
233
234
235
    where
        R: scale::Decode,
    {
236
        crate::env::eval_contract(&self.params)
237
238
239
    }
}

240
impl<E, Args, Seal> CallBuilder<E, Args, (), Seal>
241
where
242
    E: EnvTypes,
243
    Args: scale::Encode,
244
245
{
    /// Fires the cross-call to the smart contract.
246
    #[inline]
247
248
    pub fn fire(self) -> Result<()> {
        crate::env::invoke_contract(&self.params)
249
250
    }
}