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<'a, T: IntoExecutable + Clone> IntoExecutable for &'a 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().clone().into());
108        }
109        for resource in &auth_zone_init.simulate_every_proof_under_resources {
110            references.insert(resource.clone().into());
111        }
112
113        for preallocated_address in &context.pre_allocated_addresses {
114            references.insert(
115                preallocated_address
116                    .blueprint_id
117                    .package_address
118                    .clone()
119                    .into(),
120            );
121        }
122
123        Self {
124            context,
125            transaction_intent: ExecutableIntent {
126                encoded_instructions: encoded_instructions_v1,
127                references,
128                blobs,
129                auth_zone_init,
130                children_subintent_indices: vec![],
131            },
132            subintents: vec![],
133        }
134    }
135
136    pub fn new_v2(
137        mut transaction_intent: ExecutableIntent,
138        mut subintents: Vec<ExecutableIntent>,
139        context: ExecutionContext,
140    ) -> Self {
141        {
142            let intent = &mut transaction_intent;
143            for proof in &intent.auth_zone_init.initial_non_fungible_id_proofs {
144                intent
145                    .references
146                    .insert(proof.resource_address().clone().into());
147            }
148            for resource in &intent.auth_zone_init.simulate_every_proof_under_resources {
149                intent.references.insert(resource.clone().into());
150            }
151        }
152        for intent in &mut subintents {
153            for proof in &intent.auth_zone_init.initial_non_fungible_id_proofs {
154                intent
155                    .references
156                    .insert(proof.resource_address().clone().into());
157            }
158            for resource in &intent.auth_zone_init.simulate_every_proof_under_resources {
159                intent.references.insert(resource.clone().into());
160            }
161        }
162
163        // Pre-allocated addresses are currently only used by the protocol (ie genesis + protocol updates).
164        // Since there's no reason for the protocol to use child subintents, we only assign pre-allocated
165        // addresses to the root subintent
166        for preallocated_address in &context.pre_allocated_addresses {
167            transaction_intent.references.insert(
168                preallocated_address
169                    .blueprint_id
170                    .package_address
171                    .clone()
172                    .into(),
173            );
174        }
175
176        Self {
177            context,
178            transaction_intent,
179            subintents,
180        }
181    }
182
183    // Consuming builder-like customization methods:
184
185    pub fn skip_epoch_range_check(mut self) -> Self {
186        self.context.epoch_range = None;
187        self
188    }
189
190    pub fn skip_intent_hash_nullification(mut self) -> Self {
191        self.context.intent_hash_nullifications.clear();
192        self
193    }
194
195    pub fn apply_free_credit(mut self, free_credit_in_xrd: Decimal) -> Self {
196        self.context.costing_parameters.free_credit_in_xrd = free_credit_in_xrd;
197        self
198    }
199
200    pub fn unique_hash(&self) -> &Hash {
201        &self.context.unique_hash
202    }
203
204    pub fn overall_epoch_range(&self) -> Option<&EpochRange> {
205        self.context.epoch_range.as_ref()
206    }
207
208    pub fn overall_proposer_timestamp_range(&self) -> Option<&ProposerTimestampRange> {
209        self.context.proposer_timestamp_range.as_ref()
210    }
211
212    pub fn costing_parameters(&self) -> &TransactionCostingParameters {
213        &self.context.costing_parameters
214    }
215
216    pub fn pre_allocated_addresses(&self) -> &[PreAllocatedAddress] {
217        &self.context.pre_allocated_addresses
218    }
219
220    pub fn payload_size(&self) -> usize {
221        self.context.payload_size
222    }
223
224    pub fn num_of_signature_validations(&self) -> usize {
225        self.context.num_of_signature_validations
226    }
227
228    pub fn disable_limits_and_costing_modules(&self) -> bool {
229        self.context.disable_limits_and_costing_modules
230    }
231
232    pub fn transaction_intent(&self) -> &ExecutableIntent {
233        &self.transaction_intent
234    }
235
236    pub fn subintents(&self) -> &[ExecutableIntent] {
237        &self.subintents
238    }
239
240    pub fn all_intents(&self) -> impl Iterator<Item = &ExecutableIntent> {
241        iter::once(&self.transaction_intent).chain(self.subintents.iter())
242    }
243
244    pub fn intent_hash_nullifications(&self) -> &[IntentHashNullification] {
245        &self.context.intent_hash_nullifications
246    }
247
248    pub fn all_blob_hashes(&self) -> IndexSet<Hash> {
249        let mut hashes = indexset!();
250
251        for intent in self.all_intents() {
252            for hash in intent.blobs.keys() {
253                hashes.insert(*hash);
254            }
255        }
256
257        hashes
258    }
259
260    pub fn all_references(&self) -> IndexSet<Reference> {
261        let mut references = indexset!();
262
263        for intent in self.all_intents() {
264            for reference in intent.references.iter() {
265                references.insert(reference.clone());
266            }
267        }
268
269        references
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::ExecutableTransaction;
276
277    fn assert_send<T: Send>() {}
278    fn assert_sync<T: Sync>() {}
279
280    #[test]
281    fn check_executable_transaction_can_be_cached_in_the_node_mempool_and_be_shared_between_threads(
282    ) {
283        assert_send::<ExecutableTransaction>();
284        assert_sync::<ExecutableTransaction>();
285    }
286}