radix_transactions/model/execution/
executable_transaction.rs

1use std::iter;
2
3use crate::internal_prelude::*;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct ExecutableIntent {
7    pub encoded_instructions: Vec<u8>,
8    pub auth_zone_init: AuthZoneInit,
9    pub references: IndexSet<Reference>,
10    pub blobs: IndexMap<Hash, Vec<u8>>,
11    pub children_subintent_indices: Vec<SubintentIndex>,
12}
13
14/// An index of the subintent in the parent ExecutableTransaction
15/// Validation ensures that each subintent has a unique parent
16/// and a unique path from the transaction intent.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, ManifestSbor)]
18#[sbor(transparent)]
19pub struct SubintentIndex(pub usize);
20
21pub trait IntoExecutable {
22    type Error: Debug;
23
24    fn into_executable(
25        self,
26        validator: &TransactionValidator,
27    ) -> Result<ExecutableTransaction, Self::Error>;
28
29    /// For use in tests as a quick mechanism to get an executable.
30    /// Validates with a network-independent validator, using the latest settings.
31    fn into_executable_unwrap(self) -> ExecutableTransaction
32    where
33        Self: Sized,
34    {
35        self.into_executable(
36            &TransactionValidator::new_with_static_config_network_agnostic(
37                TransactionValidationConfig::latest(),
38            ),
39        )
40        .unwrap()
41    }
42}
43
44impl<T: IntoExecutable + Clone> IntoExecutable for &T {
45    type Error = T::Error;
46
47    fn into_executable(
48        self,
49        validator: &TransactionValidator,
50    ) -> Result<ExecutableTransaction, Self::Error> {
51        self.clone().into_executable(validator)
52    }
53}
54
55impl IntoExecutable for ExecutableTransaction {
56    type Error = core::convert::Infallible;
57
58    fn into_executable(
59        self,
60        _validator: &TransactionValidator,
61    ) -> Result<ExecutableTransaction, Self::Error> {
62        Ok(self)
63    }
64}
65
66/// This is an executable form of the transaction, post stateless validation.
67///
68/// An `&ExecutableTransaction` is used to execute in the engine.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct ExecutableTransaction {
71    pub(crate) transaction_intent: ExecutableIntent,
72    pub(crate) subintents: Vec<ExecutableIntent>,
73    pub(crate) context: ExecutionContext,
74}
75
76impl AsRef<ExecutableTransaction> for ExecutableTransaction {
77    fn as_ref(&self) -> &ExecutableTransaction {
78        self
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct ExecutionContext {
84    /// This is used as a source of pseudo-randomness for the id allocator and RUID generation
85    pub unique_hash: Hash,
86    pub pre_allocated_addresses: Vec<PreAllocatedAddress>,
87    pub payload_size: usize,
88    pub num_of_signature_validations: usize,
89    pub costing_parameters: TransactionCostingParameters,
90    pub epoch_range: Option<EpochRange>,
91    pub proposer_timestamp_range: Option<ProposerTimestampRange>,
92    pub disable_limits_and_costing_modules: bool,
93    pub intent_hash_nullifications: Vec<IntentHashNullification>,
94}
95
96impl ExecutableTransaction {
97    pub fn new_v1(
98        encoded_instructions_v1: Vec<u8>,
99        auth_zone_init: AuthZoneInit,
100        references: IndexSet<Reference>,
101        blobs: IndexMap<Hash, Vec<u8>>,
102        context: ExecutionContext,
103    ) -> Self {
104        let mut references = references;
105
106        for proof in &auth_zone_init.initial_non_fungible_id_proofs {
107            references.insert(proof.resource_address().into());
108        }
109        for resource in &auth_zone_init.simulate_every_proof_under_resources {
110            references.insert((*resource).into());
111        }
112
113        for preallocated_address in &context.pre_allocated_addresses {
114            references.insert(preallocated_address.blueprint_id.package_address.into());
115        }
116
117        Self {
118            context,
119            transaction_intent: ExecutableIntent {
120                encoded_instructions: encoded_instructions_v1,
121                references,
122                blobs,
123                auth_zone_init,
124                children_subintent_indices: vec![],
125            },
126            subintents: vec![],
127        }
128    }
129
130    pub fn new_v2(
131        mut transaction_intent: ExecutableIntent,
132        mut subintents: Vec<ExecutableIntent>,
133        context: ExecutionContext,
134    ) -> Self {
135        {
136            let intent = &mut transaction_intent;
137            for proof in &intent.auth_zone_init.initial_non_fungible_id_proofs {
138                intent.references.insert(proof.resource_address().into());
139            }
140            for resource in &intent.auth_zone_init.simulate_every_proof_under_resources {
141                intent.references.insert((*resource).into());
142            }
143        }
144        for intent in &mut subintents {
145            for proof in &intent.auth_zone_init.initial_non_fungible_id_proofs {
146                intent.references.insert(proof.resource_address().into());
147            }
148            for resource in &intent.auth_zone_init.simulate_every_proof_under_resources {
149                intent.references.insert((*resource).into());
150            }
151        }
152
153        // Pre-allocated addresses are currently only used by the protocol (ie genesis + protocol updates).
154        // Since there's no reason for the protocol to use child subintents, we only assign pre-allocated
155        // addresses to the root subintent
156        for preallocated_address in &context.pre_allocated_addresses {
157            transaction_intent
158                .references
159                .insert(preallocated_address.blueprint_id.package_address.into());
160        }
161
162        Self {
163            context,
164            transaction_intent,
165            subintents,
166        }
167    }
168
169    // Consuming builder-like customization methods:
170
171    pub fn skip_epoch_range_check(mut self) -> Self {
172        self.context.epoch_range = None;
173        self
174    }
175
176    pub fn skip_intent_hash_nullification(mut self) -> Self {
177        self.context.intent_hash_nullifications.clear();
178        self
179    }
180
181    pub fn apply_free_credit(mut self, free_credit_in_xrd: Decimal) -> Self {
182        self.context.costing_parameters.free_credit_in_xrd = free_credit_in_xrd;
183        self
184    }
185
186    pub fn unique_hash(&self) -> &Hash {
187        &self.context.unique_hash
188    }
189
190    pub fn overall_epoch_range(&self) -> Option<&EpochRange> {
191        self.context.epoch_range.as_ref()
192    }
193
194    pub fn overall_proposer_timestamp_range(&self) -> Option<&ProposerTimestampRange> {
195        self.context.proposer_timestamp_range.as_ref()
196    }
197
198    pub fn costing_parameters(&self) -> &TransactionCostingParameters {
199        &self.context.costing_parameters
200    }
201
202    pub fn pre_allocated_addresses(&self) -> &[PreAllocatedAddress] {
203        &self.context.pre_allocated_addresses
204    }
205
206    pub fn payload_size(&self) -> usize {
207        self.context.payload_size
208    }
209
210    pub fn num_of_signature_validations(&self) -> usize {
211        self.context.num_of_signature_validations
212    }
213
214    pub fn disable_limits_and_costing_modules(&self) -> bool {
215        self.context.disable_limits_and_costing_modules
216    }
217
218    pub fn transaction_intent(&self) -> &ExecutableIntent {
219        &self.transaction_intent
220    }
221
222    pub fn subintents(&self) -> &[ExecutableIntent] {
223        &self.subintents
224    }
225
226    pub fn all_intents(&self) -> impl Iterator<Item = &ExecutableIntent> {
227        iter::once(&self.transaction_intent).chain(self.subintents.iter())
228    }
229
230    pub fn intent_hash_nullifications(&self) -> &[IntentHashNullification] {
231        &self.context.intent_hash_nullifications
232    }
233
234    pub fn all_blob_hashes(&self) -> IndexSet<Hash> {
235        let mut hashes = indexset!();
236
237        for intent in self.all_intents() {
238            for hash in intent.blobs.keys() {
239                hashes.insert(*hash);
240            }
241        }
242
243        hashes
244    }
245
246    pub fn all_references(&self) -> IndexSet<Reference> {
247        let mut references = indexset!();
248
249        for intent in self.all_intents() {
250            for reference in intent.references.iter() {
251                references.insert(*reference);
252            }
253        }
254
255        references
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::ExecutableTransaction;
262
263    fn assert_send<T: Send>() {}
264    fn assert_sync<T: Sync>() {}
265
266    #[test]
267    fn check_executable_transaction_can_be_cached_in_the_node_mempool_and_be_shared_between_threads(
268    ) {
269        assert_send::<ExecutableTransaction>();
270        assert_sync::<ExecutableTransaction>();
271    }
272}