1use crate::internal_prelude::*;
2
3#[derive(ManifestSbor)]
4pub enum TestTransaction {
5 V1(TestIntentV1),
6 V2 {
7 root_intent: TestIntentV2,
8 subintents: Vec<TestIntentV2>,
9 },
10}
11
12pub struct TestTransactionV2Builder {
13 nonce: u32,
14 subintents: IndexMap<SubintentHash, TestIntentV2>,
15}
16
17impl TestTransactionV2Builder {
18 pub fn new(nonce: u32) -> Self {
19 Self {
20 nonce,
21 subintents: Default::default(),
22 }
23 }
24
25 pub fn add_simple_subintent(
27 &mut self,
28 children: impl IntoIterator<Item = SubintentHash>,
29 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
30 ) -> SubintentHash {
31 let mut manifest_builder = ManifestBuilder::new_subintent_v2();
32 for (child_index, child_hash) in children.into_iter().enumerate() {
33 let child_name = format!("child_{child_index}");
34 manifest_builder = manifest_builder.use_child(&child_name, child_hash);
35 manifest_builder = manifest_builder.yield_to_child(child_name, ());
36 }
37 let manifest = manifest_builder.yield_to_parent(()).build();
38 self.add_subintent(manifest, proofs)
39 }
40
41 pub fn add_tweaked_simple_subintent(
42 &mut self,
43 children: impl IntoIterator<Item = SubintentHash>,
44 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
45 addition: impl FnOnce(SubintentManifestV2Builder) -> SubintentManifestV2Builder,
46 ) -> SubintentHash {
47 let mut manifest_builder = ManifestBuilder::new_subintent_v2();
48 for (child_index, child_hash) in children.into_iter().enumerate() {
49 let child_name = format!("child_{child_index}");
50 manifest_builder = manifest_builder.use_child(&child_name, child_hash);
51 manifest_builder = manifest_builder.yield_to_child(child_name, ());
52 }
53 manifest_builder = addition(manifest_builder);
54 let manifest = manifest_builder.yield_to_parent(()).build();
55 self.add_subintent(manifest, proofs)
56 }
57
58 pub fn add_subintent(
59 &mut self,
60 manifest: SubintentManifestV2,
61 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
62 ) -> SubintentHash {
63 let (instructions, blobs, child_intents) = manifest.for_intent();
64 let intent = self.create_intent(instructions, blobs, child_intents, proofs);
65 let hash = intent.hash;
66 self.subintents.insert(SubintentHash(hash), intent);
67 SubintentHash(hash)
68 }
69
70 pub fn finish_with_simple_root_intent(
72 self,
73 children: impl IntoIterator<Item = SubintentHash>,
74 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
75 ) -> TestTransaction {
76 let mut manifest_builder = ManifestBuilder::new_v2();
77 manifest_builder = manifest_builder.lock_fee_from_faucet();
78 for (child_index, child_hash) in children.into_iter().enumerate() {
79 let child_name = format!("child_{child_index}");
80 manifest_builder = manifest_builder.use_child(&child_name, child_hash);
82 manifest_builder = manifest_builder.yield_to_child(child_name, ());
83 }
84 let manifest = manifest_builder.build();
85 self.finish_with_root_intent(manifest, proofs)
86 }
87
88 pub fn finish_with_root_intent(
89 self,
90 manifest: TransactionManifestV2,
91 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
92 ) -> TestTransaction {
93 let (instructions, blobs, child_intents) = manifest.for_intent();
94 let root_intent = self.create_intent(instructions, blobs, child_intents, proofs);
95 TestTransaction::V2 {
96 root_intent,
97 subintents: self.subintents.into_values().collect(),
98 }
99 }
100
101 fn create_intent(
102 &self,
103 instructions: InstructionsV2,
104 blobs: BlobsV1,
105 child_intents: ChildSubintentSpecifiersV2,
106 proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
107 ) -> TestIntentV2 {
108 let children_subintent_indices = child_intents
109 .children
110 .into_iter()
111 .map(|child| {
112 let subintent_index = self
113 .subintents
114 .get_index_of(&child.hash)
115 .expect("Child subintents should exist already in the Test Transaction");
116 SubintentIndex(subintent_index)
117 })
118 .collect();
119 let nonce = self.nonce;
120 let subintent_count = self.subintents.len();
121 let hash = hash(format!(
122 "Test transaction intent: {nonce} - {subintent_count}"
123 ));
124 TestIntentV2 {
125 instructions,
126 blobs,
127 hash,
128 initial_proofs: proofs.into_iter().collect(),
129 children_subintent_indices,
130 }
131 }
132}
133
134#[derive(ManifestSbor)]
135pub struct TestIntentV1 {
136 pub instructions: InstructionsV1,
137 pub blobs: BlobsV1,
138 pub hash: Hash,
139 pub initial_proofs: BTreeSet<NonFungibleGlobalId>,
140}
141
142#[derive(ManifestSbor)]
143pub struct TestIntentV2 {
144 pub instructions: InstructionsV2,
145 pub blobs: BlobsV1,
146 pub hash: Hash,
147 pub initial_proofs: BTreeSet<NonFungibleGlobalId>,
148 pub children_subintent_indices: Vec<SubintentIndex>,
149}
150
151pub enum PreparedTestTransaction {
152 V1(PreparedTestIntent),
153 V2 {
154 root_intent: PreparedTestIntent,
155 subintents: Vec<PreparedTestIntent>,
156 },
157}
158
159pub struct PreparedTestIntent {
160 pub encoded_instructions: Vec<u8>,
161 pub references: IndexSet<Reference>,
162 pub blobs: IndexMap<Hash, Vec<u8>>,
163 pub hash: Hash,
164 pub children_subintent_indices: Vec<SubintentIndex>,
165 pub initial_proofs: BTreeSet<NonFungibleGlobalId>,
166}
167
168impl PreparedTestIntent {
169 #[allow(deprecated)]
170 pub fn from_v1(
171 intent: TestIntentV1,
172 settings: &PreparationSettings,
173 ) -> Result<Self, PrepareError> {
174 let prepared_instructions = intent.instructions.prepare_partial(settings)?;
175 Ok(PreparedTestIntent {
176 encoded_instructions: manifest_encode(&prepared_instructions.inner.0)?,
177 references: prepared_instructions.references,
178 blobs: intent.blobs.prepare_partial(settings)?.blobs_by_hash,
179 hash: intent.hash,
180 children_subintent_indices: vec![],
181 initial_proofs: intent.initial_proofs,
182 })
183 }
184
185 pub fn from_v2(
186 intent: TestIntentV2,
187 settings: &PreparationSettings,
188 ) -> Result<Self, PrepareError> {
189 let prepared_instructions = intent.instructions.prepare_partial(settings)?;
190 Ok(PreparedTestIntent {
191 encoded_instructions: manifest_encode(&prepared_instructions.inner.0)?,
192 references: prepared_instructions.references,
193 blobs: intent.blobs.prepare_partial(settings)?.blobs_by_hash,
194 hash: intent.hash,
195 children_subintent_indices: intent.children_subintent_indices,
196 initial_proofs: intent.initial_proofs,
197 })
198 }
199
200 pub fn into_executable_intent(self) -> ExecutableIntent {
201 let auth_zone_init = AuthZoneInit::proofs(self.initial_proofs);
202
203 ExecutableIntent {
204 encoded_instructions: self.encoded_instructions,
205 auth_zone_init,
206 references: self.references,
207 blobs: self.blobs,
208 children_subintent_indices: self.children_subintent_indices,
209 }
210 }
211}
212
213impl TestTransaction {
214 pub fn new_v1_from_nonce(
216 manifest: TransactionManifestV1,
217 nonce: u32,
218 initial_proofs: BTreeSet<NonFungibleGlobalId>,
219 ) -> Self {
220 Self::new_v1(
221 manifest,
222 hash(format!("Test transaction: {}", nonce)),
223 initial_proofs,
224 )
225 }
226
227 pub fn new_from_any_manifest(
228 any_manifest: AnyManifest,
229 nonce: u32,
230 initial_proofs: BTreeSet<NonFungibleGlobalId>,
231 ) -> Result<Self, String> {
232 match any_manifest {
233 AnyManifest::V1(manifest) => {
234 Ok(Self::new_v1_from_nonce(manifest, nonce, initial_proofs))
235 }
236 AnyManifest::SystemV1(_) => {
237 Err("Cannot convert a system manifest to a test transaction".to_string())
238 }
239 AnyManifest::V2(manifest) => {
240 Ok(Self::new_v2_builder(nonce).finish_with_root_intent(manifest, initial_proofs))
241 }
242 AnyManifest::SubintentV2(_) => {
243 Err("Cannot convert a subintent manifest to a test transaction".to_string())
244 }
245 }
246 }
247
248 pub fn new_v1(
249 manifest: TransactionManifestV1,
250 hash: Hash,
251 initial_proofs: BTreeSet<NonFungibleGlobalId>,
252 ) -> Self {
253 let (instructions, blobs) = manifest.for_intent();
254 Self::V1(TestIntentV1 {
255 instructions,
256 blobs,
257 hash,
258 initial_proofs,
259 })
260 }
261
262 pub fn new_v2_builder(nonce: u32) -> TestTransactionV2Builder {
287 TestTransactionV2Builder::new(nonce)
288 }
289
290 #[allow(deprecated)]
291 pub fn prepare(
292 self,
293 settings: &PreparationSettings,
294 ) -> Result<PreparedTestTransaction, PrepareError> {
295 match self {
296 Self::V1(intent) => Ok(PreparedTestTransaction::V1(PreparedTestIntent::from_v1(
297 intent, settings,
298 )?)),
299 Self::V2 {
300 root_intent,
301 subintents,
302 } => Ok(PreparedTestTransaction::V2 {
303 root_intent: PreparedTestIntent::from_v2(root_intent, settings)?,
304 subintents: subintents
305 .into_iter()
306 .map(|intent| PreparedTestIntent::from_v2(intent, settings))
307 .collect::<Result<_, _>>()?,
308 }),
309 }
310 }
311}
312
313impl IntoExecutable for TestTransaction {
314 type Error = PrepareError;
315
316 fn into_executable(
317 self,
318 validator: &TransactionValidator,
319 ) -> Result<ExecutableTransaction, Self::Error> {
320 Ok(self
321 .prepare(validator.preparation_settings())?
322 .into_unvalidated_executable())
323 }
324}
325
326impl PreparedTestTransaction {
327 pub fn into_unvalidated_executable(self) -> ExecutableTransaction {
328 match self {
329 PreparedTestTransaction::V1(intent) => {
330 let num_of_signature_validations = intent.initial_proofs.len() + 1;
331 ExecutableTransaction::new_v1(
332 intent.encoded_instructions.clone(),
333 AuthZoneInit::proofs(intent.initial_proofs.clone()),
334 intent.references.clone(),
335 intent.blobs.clone(),
336 ExecutionContext {
337 unique_hash: intent.hash,
338 intent_hash_nullifications: vec![],
339 epoch_range: None,
340 payload_size: intent.encoded_instructions.len()
341 + intent.blobs.values().map(|x| x.len()).sum::<usize>(),
342 num_of_signature_validations,
344 costing_parameters: TransactionCostingParameters {
345 tip: TipSpecifier::None,
346 free_credit_in_xrd: Decimal::ZERO,
347 },
348 pre_allocated_addresses: vec![],
349 disable_limits_and_costing_modules: false,
350 proposer_timestamp_range: None,
351 },
352 )
353 }
354 PreparedTestTransaction::V2 {
355 root_intent,
356 subintents,
357 } => {
358 let all_intents = core::iter::once(&root_intent)
359 .chain(subintents.iter())
360 .collect::<Vec<_>>();
361 let payload_size = all_intents
362 .iter()
363 .map(|intent| {
364 intent.encoded_instructions.len()
365 + intent.blobs.values().map(|x| x.len()).sum::<usize>()
366 })
367 .sum();
368 let num_of_signature_validations = all_intents
369 .iter()
370 .map(|intent| intent.initial_proofs.len())
371 .sum();
372
373 let context = ExecutionContext {
374 unique_hash: root_intent.hash,
375 intent_hash_nullifications: vec![],
376 epoch_range: None,
377 payload_size,
378 num_of_signature_validations,
380 costing_parameters: TransactionCostingParameters {
381 tip: TipSpecifier::None,
382 free_credit_in_xrd: Decimal::ZERO,
383 },
384 pre_allocated_addresses: vec![],
385 disable_limits_and_costing_modules: false,
386 proposer_timestamp_range: None,
387 };
388
389 ExecutableTransaction::new_v2(
390 root_intent.into_executable_intent(),
391 subintents
392 .into_iter()
393 .map(|intent| intent.into_executable_intent())
394 .collect(),
395 context,
396 )
397 }
398 }
399 }
400}
401
402impl IntoExecutable for PreparedTestTransaction {
403 type Error = core::convert::Infallible;
404
405 fn into_executable(
406 self,
407 _validator: &TransactionValidator,
408 ) -> Result<ExecutableTransaction, Self::Error> {
409 Ok(self.into_unvalidated_executable())
410 }
411}