fuel_vm/checked_transaction/
types.rs

1//! Implementation for different transaction types, groupd in submodules.
2
3pub use self::{
4    blob::CheckedMetadata as BlobCheckedMetadata,
5    create::CheckedMetadata as CreateCheckedMetadata,
6    script::CheckedMetadata as ScriptCheckedMetadata,
7    upgrade::CheckedMetadata as UpgradeCheckedMetadata,
8    upload::CheckedMetadata as UploadCheckedMetadata,
9};
10use alloc::collections::BTreeMap;
11use fuel_types::{
12    AssetId,
13    Word,
14};
15
16/// The spendable unrestricted initial assets.
17/// More information about it in the specification:
18/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/tx-validity.md#sufficient-balance>
19#[derive(Default, Debug, Clone, Eq, PartialEq, Hash)]
20pub struct NonRetryableFreeBalances(pub(crate) BTreeMap<AssetId, Word>);
21
22impl From<NonRetryableFreeBalances> for BTreeMap<AssetId, Word> {
23    fn from(value: NonRetryableFreeBalances) -> Self {
24        value.0
25    }
26}
27
28impl core::ops::Deref for NonRetryableFreeBalances {
29    type Target = BTreeMap<AssetId, Word>;
30
31    fn deref(&self) -> &Self::Target {
32        &self.0
33    }
34}
35
36/// The spendable only during execution [`AssetId::BASE`] asset.
37/// More information about it in the specification:
38/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/tx-validity.md#sufficient-balance>
39#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
40pub struct RetryableAmount {
41    pub(crate) amount: Word,
42    pub(crate) base_asset_id: AssetId,
43}
44
45impl From<RetryableAmount> for Word {
46    fn from(value: RetryableAmount) -> Self {
47        value.amount
48    }
49}
50
51impl core::ops::Deref for RetryableAmount {
52    type Target = Word;
53
54    fn deref(&self) -> &Self::Target {
55        &self.amount
56    }
57}
58
59/// For [`fuel_tx::Create`]
60pub mod create {
61    use super::super::{
62        balances::{
63            initial_free_balances,
64            AvailableBalances,
65        },
66        Checked,
67        IntoChecked,
68    };
69    use crate::checked_transaction::{
70        CheckError,
71        NonRetryableFreeBalances,
72    };
73    use fuel_tx::{
74        Cacheable,
75        Chargeable,
76        ConsensusParameters,
77        Create,
78        FormatValidityChecks,
79    };
80    use fuel_types::{
81        AssetId,
82        BlockHeight,
83    };
84
85    /// Metadata produced by checking [`fuel_tx::Create`].
86    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
87    pub struct CheckedMetadata {
88        /// The base asset id.
89        pub base_asset_id: AssetId,
90        /// See [`NonRetryableFreeBalances`].
91        pub free_balances: NonRetryableFreeBalances,
92        /// The block height this tx was verified with
93        pub block_height: BlockHeight,
94        /// The minimum gas required for this transaction.
95        pub min_gas: u64,
96        /// The maximum gas required for this transaction.
97        pub max_gas: u64,
98    }
99
100    impl IntoChecked for Create {
101        type Metadata = CheckedMetadata;
102
103        fn into_checked_basic(
104            mut self,
105            block_height: BlockHeight,
106            consensus_params: &ConsensusParameters,
107        ) -> Result<Checked<Self>, CheckError> {
108            let chain_id = consensus_params.chain_id();
109            self.precompute(&chain_id)?;
110            self.check_without_signatures(block_height, consensus_params)?;
111
112            // validate fees and compute free balances
113            let AvailableBalances {
114                non_retryable_balances,
115                retryable_balance,
116            } = initial_free_balances(&self, consensus_params.base_asset_id())?;
117            debug_assert_eq!(
118                retryable_balance, 0,
119                "The `check_without_signatures` should return `TransactionInputContainsMessageData` above"
120            );
121
122            let metadata = CheckedMetadata {
123                base_asset_id: *consensus_params.base_asset_id(),
124                free_balances: NonRetryableFreeBalances(non_retryable_balances),
125                block_height,
126                min_gas: self
127                    .min_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
128                max_gas: self
129                    .max_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
130            };
131
132            Ok(Checked::basic(self, metadata))
133        }
134    }
135}
136
137/// For [`fuel_tx::Mint`]
138pub mod mint {
139    use super::super::{
140        Checked,
141        IntoChecked,
142    };
143    use crate::checked_transaction::CheckError;
144    use fuel_tx::{
145        Cacheable,
146        ConsensusParameters,
147        FormatValidityChecks,
148        Mint,
149    };
150    use fuel_types::BlockHeight;
151
152    impl IntoChecked for Mint {
153        type Metadata = ();
154
155        fn into_checked_basic(
156            mut self,
157            block_height: BlockHeight,
158            consensus_params: &ConsensusParameters,
159        ) -> Result<Checked<Self>, CheckError> {
160            let chain_id = consensus_params.chain_id();
161            self.precompute(&chain_id)?;
162            self.check_without_signatures(block_height, consensus_params)?;
163
164            Ok(Checked::basic(self, ()))
165        }
166    }
167}
168
169/// For [`fuel_tx::Script`]
170pub mod script {
171    use super::super::{
172        balances::{
173            initial_free_balances,
174            AvailableBalances,
175        },
176        Checked,
177        IntoChecked,
178    };
179    use crate::checked_transaction::{
180        CheckError,
181        NonRetryableFreeBalances,
182        RetryableAmount,
183    };
184    use fuel_tx::{
185        Cacheable,
186        Chargeable,
187        ConsensusParameters,
188        FormatValidityChecks,
189        Script,
190    };
191    use fuel_types::{
192        AssetId,
193        BlockHeight,
194    };
195
196    /// Metadata produced by checking [`fuel_tx::Script`].
197    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
198    pub struct CheckedMetadata {
199        /// The base asset id.
200        pub base_asset_id: AssetId,
201        /// See [`NonRetryableFreeBalances`].
202        pub non_retryable_balances: NonRetryableFreeBalances,
203        /// See [`RetryableAmount`].
204        pub retryable_balance: RetryableAmount,
205        /// The block height this tx was verified with
206        pub block_height: BlockHeight,
207        /// The minimum gas required for this transaction.
208        pub min_gas: u64,
209        /// The maximum gas required for this transaction.
210        pub max_gas: u64,
211    }
212
213    impl IntoChecked for Script {
214        type Metadata = CheckedMetadata;
215
216        fn into_checked_basic(
217            mut self,
218            block_height: BlockHeight,
219            consensus_params: &ConsensusParameters,
220        ) -> Result<Checked<Self>, CheckError> {
221            let chain_id = consensus_params.chain_id();
222            self.precompute(&chain_id)?;
223            self.check_without_signatures(block_height, consensus_params)?;
224
225            // validate fees and compute free balances
226            let AvailableBalances {
227                non_retryable_balances,
228                retryable_balance,
229            } = initial_free_balances(&self, consensus_params.base_asset_id())?;
230
231            let metadata = CheckedMetadata {
232                base_asset_id: *consensus_params.base_asset_id(),
233                non_retryable_balances: NonRetryableFreeBalances(non_retryable_balances),
234                retryable_balance: RetryableAmount {
235                    amount: retryable_balance,
236                    base_asset_id: *consensus_params.base_asset_id(),
237                },
238                block_height,
239                min_gas: self
240                    .min_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
241                max_gas: self
242                    .max_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
243            };
244
245            Ok(Checked::basic(self, metadata))
246        }
247    }
248}
249
250/// For [`fuel_tx::Upgrade`]
251pub mod upgrade {
252    use super::super::{
253        balances::{
254            initial_free_balances,
255            AvailableBalances,
256        },
257        Checked,
258        IntoChecked,
259    };
260    use crate::checked_transaction::{
261        CheckError,
262        NonRetryableFreeBalances,
263    };
264    use fuel_tx::{
265        Cacheable,
266        Chargeable,
267        ConsensusParameters,
268        FormatValidityChecks,
269        Upgrade,
270    };
271    use fuel_types::{
272        AssetId,
273        BlockHeight,
274    };
275
276    /// Metadata produced by checking [`fuel_tx::Upgrade`].
277    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
278    pub struct CheckedMetadata {
279        /// The base asset id.
280        pub base_asset_id: AssetId,
281        /// See [`NonRetryableFreeBalances`].
282        pub free_balances: NonRetryableFreeBalances,
283        /// The block height this tx was verified with
284        pub block_height: BlockHeight,
285        /// The minimum gas required for this transaction.
286        pub min_gas: u64,
287        /// The maximum gas required for this transaction.
288        pub max_gas: u64,
289    }
290
291    impl IntoChecked for Upgrade {
292        type Metadata = CheckedMetadata;
293
294        fn into_checked_basic(
295            mut self,
296            block_height: BlockHeight,
297            consensus_params: &ConsensusParameters,
298        ) -> Result<Checked<Self>, CheckError> {
299            let chain_id = consensus_params.chain_id();
300            self.precompute(&chain_id)?;
301            self.check_without_signatures(block_height, consensus_params)?;
302
303            // validate fees and compute free balances
304            let AvailableBalances {
305                non_retryable_balances,
306                retryable_balance,
307            } = initial_free_balances(&self, consensus_params.base_asset_id())?;
308            debug_assert_eq!(
309                retryable_balance, 0,
310                "The `check_without_signatures` should return `TransactionInputContainsMessageData` above"
311            );
312
313            let metadata = CheckedMetadata {
314                base_asset_id: *consensus_params.base_asset_id(),
315                free_balances: NonRetryableFreeBalances(non_retryable_balances),
316                block_height,
317                min_gas: self
318                    .min_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
319                max_gas: self
320                    .max_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
321            };
322
323            Ok(Checked::basic(self, metadata))
324        }
325    }
326}
327
328/// For [`fuel_tx::Upload`]
329pub mod upload {
330    use super::super::{
331        balances::{
332            initial_free_balances,
333            AvailableBalances,
334        },
335        Checked,
336        IntoChecked,
337    };
338    use crate::checked_transaction::{
339        CheckError,
340        NonRetryableFreeBalances,
341    };
342    use fuel_tx::{
343        Cacheable,
344        Chargeable,
345        ConsensusParameters,
346        FormatValidityChecks,
347        Upload,
348    };
349    use fuel_types::{
350        AssetId,
351        BlockHeight,
352    };
353
354    /// Metadata produced by checking [`fuel_tx::Upload`].
355    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
356    pub struct CheckedMetadata {
357        /// The base asset id.
358        pub base_asset_id: AssetId,
359        /// See [`NonRetryableFreeBalances`].
360        pub free_balances: NonRetryableFreeBalances,
361        /// The block height this tx was verified with
362        pub block_height: BlockHeight,
363        /// The minimum gas required for this transaction.
364        pub min_gas: u64,
365        /// The maximum gas required for this transaction.
366        pub max_gas: u64,
367    }
368
369    impl IntoChecked for Upload {
370        type Metadata = CheckedMetadata;
371
372        fn into_checked_basic(
373            mut self,
374            block_height: BlockHeight,
375            consensus_params: &ConsensusParameters,
376        ) -> Result<Checked<Self>, CheckError> {
377            let chain_id = consensus_params.chain_id();
378            self.precompute(&chain_id)?;
379            self.check_without_signatures(block_height, consensus_params)?;
380
381            // validate fees and compute free balances
382            let AvailableBalances {
383                non_retryable_balances,
384                retryable_balance,
385            } = initial_free_balances(&self, consensus_params.base_asset_id())?;
386            debug_assert_eq!(
387                retryable_balance, 0,
388                "The `check_without_signatures` should return `TransactionInputContainsMessageData` above"
389            );
390
391            let metadata = CheckedMetadata {
392                base_asset_id: *consensus_params.base_asset_id(),
393                free_balances: NonRetryableFreeBalances(non_retryable_balances),
394                block_height,
395                min_gas: self
396                    .min_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
397                max_gas: self
398                    .max_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
399            };
400
401            Ok(Checked::basic(self, metadata))
402        }
403    }
404}
405
406/// For [`fuel_tx::Blob`]
407pub mod blob {
408    use super::super::{
409        balances::{
410            initial_free_balances,
411            AvailableBalances,
412        },
413        Checked,
414        IntoChecked,
415    };
416    use crate::checked_transaction::{
417        CheckError,
418        NonRetryableFreeBalances,
419    };
420    use fuel_tx::{
421        AssetId,
422        Blob,
423        Cacheable,
424        Chargeable,
425        ConsensusParameters,
426        FormatValidityChecks,
427    };
428    use fuel_types::BlockHeight;
429
430    /// Metadata produced by checking [`fuel_tx::Blob`].
431    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
432    pub struct CheckedMetadata {
433        /// The base asset id.
434        pub base_asset_id: AssetId,
435        /// See [`NonRetryableFreeBalances`].
436        pub free_balances: NonRetryableFreeBalances,
437        /// The block height this tx was verified with
438        pub block_height: BlockHeight,
439        /// The minimum gas required for this transaction.
440        pub min_gas: u64,
441        /// The maximum gas required for this transaction.
442        pub max_gas: u64,
443    }
444
445    impl IntoChecked for Blob {
446        type Metadata = CheckedMetadata;
447
448        fn into_checked_basic(
449            mut self,
450            block_height: BlockHeight,
451            consensus_params: &ConsensusParameters,
452        ) -> Result<Checked<Self>, CheckError> {
453            let chain_id = consensus_params.chain_id();
454            self.precompute(&chain_id)?;
455            self.check_without_signatures(block_height, consensus_params)?;
456
457            // validate fees and compute free balances
458            let AvailableBalances {
459                non_retryable_balances,
460                retryable_balance,
461            } = initial_free_balances(&self, consensus_params.base_asset_id())?;
462            debug_assert_eq!(
463                retryable_balance, 0,
464                "The `check_without_signatures` should return `TransactionInputContainsMessageData` above"
465            );
466
467            let metadata = CheckedMetadata {
468                base_asset_id: *consensus_params.base_asset_id(),
469                free_balances: NonRetryableFreeBalances(non_retryable_balances),
470                block_height,
471                min_gas: self
472                    .min_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
473                max_gas: self
474                    .max_gas(consensus_params.gas_costs(), consensus_params.fee_params()),
475            };
476
477            Ok(Checked::basic(self, metadata))
478        }
479    }
480}