radix_transactions/builder/
transaction_builder.rs

1use crate::internal_prelude::*;
2use crate::model::*;
3use crate::signing::Signer;
4
5//====================================
6// This file contains:
7// * TransactionV1Builder (with alias TransactionBuilder), which creates:
8//   - TransactionIntentV1
9//   - SignedTransactionIntentV1
10//   - NotarizedTransactionV1
11// * PartialTransactionV2Builder, which creates:
12//   - SubintentV2
13//   - SignedPartialTransactionV2
14// * TransactionV2Builder, which creates:
15//   - TransactionIntentV2
16//   - SignedTransactionIntentV2
17//   - NotarizedTransactionV2
18//====================================
19pub type TransactionBuilder = TransactionV1Builder;
20
21impl TransactionBuilder {
22    // In symmetry with the ManifestBuilder, we add in some methods on the V1 builder
23    // to create the V2 builders.
24
25    pub fn new_v2() -> TransactionV2Builder {
26        TransactionV2Builder::new()
27    }
28
29    pub fn new_partial_v2() -> PartialTransactionV2Builder {
30        PartialTransactionV2Builder::new()
31    }
32}
33
34#[derive(Clone)]
35pub struct TransactionV1Builder {
36    manifest: Option<TransactionManifestV1>,
37    header: Option<TransactionHeaderV1>,
38    message: Option<MessageV1>,
39    intent_signatures: Vec<SignatureWithPublicKeyV1>,
40    notary_signature: Option<SignatureV1>,
41}
42
43impl TransactionV1Builder {
44    pub fn new() -> Self {
45        Self {
46            manifest: None,
47            header: None,
48            message: None,
49            intent_signatures: vec![],
50            notary_signature: None,
51        }
52    }
53
54    pub fn then(self, next: impl FnOnce(Self) -> Self) -> Self {
55        next(self)
56    }
57
58    pub fn manifest(mut self, manifest: TransactionManifestV1) -> Self {
59        self.manifest = Some(manifest);
60        self
61    }
62
63    pub fn header(mut self, header: TransactionHeaderV1) -> Self {
64        self.header = Some(header);
65        self
66    }
67
68    pub fn message(mut self, message: MessageV1) -> Self {
69        self.message = Some(message);
70        self
71    }
72
73    pub fn sign<S: Signer>(mut self, signer: S) -> Self {
74        let intent = self.transaction_intent();
75        let prepared = intent
76            .prepare(PreparationSettings::latest_ref())
77            .expect("Intent could be prepared");
78        self.intent_signatures
79            .push(signer.sign_with_public_key(&prepared.transaction_intent_hash()));
80        self
81    }
82
83    pub fn multi_sign<S: Signer>(mut self, signers: impl IntoIterator<Item = S>) -> Self {
84        let intent = self.transaction_intent();
85        let prepared = intent
86            .prepare(PreparationSettings::latest_ref())
87            .expect("Intent could be prepared");
88        for signer in signers {
89            self.intent_signatures
90                .push(signer.sign_with_public_key(&prepared.transaction_intent_hash()));
91        }
92        self
93    }
94
95    pub fn signer_signatures(mut self, sigs: Vec<SignatureWithPublicKeyV1>) -> Self {
96        self.intent_signatures.extend(sigs);
97        self
98    }
99
100    pub fn notarize<S: Signer>(mut self, signer: S) -> Self {
101        let signed_intent = self.signed_transaction_intent();
102        let prepared = signed_intent
103            .prepare(PreparationSettings::latest_ref())
104            .expect("Signed intent could be prepared");
105        self.notary_signature = Some(
106            signer
107                .sign_with_public_key(&prepared.signed_transaction_intent_hash())
108                .signature(),
109        );
110        self
111    }
112
113    pub fn notary_signature(mut self, signature: SignatureV1) -> Self {
114        self.notary_signature = Some(signature);
115        self
116    }
117
118    pub fn build(&self) -> NotarizedTransactionV1 {
119        NotarizedTransactionV1 {
120            signed_intent: self.signed_transaction_intent(),
121            notary_signature: NotarySignatureV1(
122                self.notary_signature.clone().expect("Not notarized"),
123            ),
124        }
125    }
126
127    pub fn into_manifest(self) -> TransactionManifestV1 {
128        self.manifest.expect("No manifest")
129    }
130
131    fn transaction_intent(&self) -> IntentV1 {
132        let (instructions, blobs) = self
133            .manifest
134            .clone()
135            .expect("Manifest not specified")
136            .for_intent();
137        IntentV1 {
138            header: self.header.clone().expect("Header not specified"),
139            instructions,
140            blobs,
141            message: self.message.clone().unwrap_or(MessageV1::None),
142        }
143    }
144
145    fn signed_transaction_intent(&self) -> SignedIntentV1 {
146        let intent = self.transaction_intent();
147        SignedIntentV1 {
148            intent,
149            intent_signatures: IntentSignaturesV1 {
150                signatures: self
151                    .intent_signatures
152                    .clone()
153                    .into_iter()
154                    .map(|sig| IntentSignatureV1(sig))
155                    .collect(),
156            },
157        }
158    }
159}
160
161// We alow either to avoid confusion
162pub type SignedPartialTransactionV2Builder = PartialTransactionV2Builder;
163
164/// A builder for a [`SignedPartialTransactionV2`].
165///
166/// This should be used in the following order:
167///
168/// * Configure the root subintent:
169///   * Set the [`intent_header`][Self::intent_header]
170///   * Optionally, set the [`message`][Self::message]
171///   * Optionally, add one or more signed partial transaction children [`add_signed_child`][Self::add_signed_child].
172///     These can themseleves be created with the [`PartialTransactionV2Builder`] methods [`build_with_names()`][PartialTransactionV2Builder::build_with_names] or
173///     [`build`][PartialTransactionV2Builder::build].
174///   * Set the manifest with [`manifest_builder`][Self::manifest_builder].
175/// * Sign the root subintent zero or more times:
176///   * Use methods [`sign`][Self::sign] or [`multi_sign`][Self::multi_sign] [`add_signature`][Self::add_signature]
177/// * Build:
178///   * Use method [`build`][Self::build], [`build_no_validate`][Self::build_no_validate],
179///     [`build_minimal`][Self::build_minimal] or [`build_minimal_no_validate`][Self::build_minimal_no_validate]
180///
181/// The error messages aren't great if used out of order.
182/// In future, this may become a state-machine style builder, to catch more errors at compile time.
183#[derive(Default, Clone)]
184pub struct PartialTransactionV2Builder {
185    pub(crate) child_partial_transactions: IndexMap<
186        String,
187        (
188            SubintentHash,
189            SignedPartialTransactionV2,
190            TransactionObjectNames,
191        ),
192    >,
193    pub(crate) root_subintent_header: Option<IntentHeaderV2>,
194    pub(crate) root_subintent_message: Option<MessageV2>,
195    pub(crate) root_subintent_manifest: Option<SubintentManifestV2>,
196    // Cached once created
197    root_subintent: Option<(SubintentV2, ManifestObjectNames)>,
198    prepared_root_subintent: Option<PreparedSubintentV2>,
199    root_subintent_signatures: Vec<IntentSignatureV1>,
200}
201
202impl PartialTransactionV2Builder {
203    pub fn new() -> Self {
204        Default::default()
205    }
206
207    pub fn then(self, next: impl FnOnce(Self) -> Self) -> Self {
208        next(self)
209    }
210
211    /// When used with the [`manifest_builder`][Self::manifest_builder] method, the provided name and hash
212    /// are provided automatically via [`use_child`][ManifestBuilder::use_child] at the start of manifest creation.
213    ///
214    /// When used with the [`manifest`][Self::manifest] method, the provided name is simply ignored - names
215    /// are returned from the provided manifest.
216    pub fn add_signed_child(
217        mut self,
218        name: impl AsRef<str>,
219        signed_partial_transaction: impl ResolvableSignedPartialTransaction,
220    ) -> Self {
221        if self.root_subintent_manifest.is_some() {
222            panic!("Call add_signed_child before calling manifest or manifest_builder");
223        }
224        let (signed_partial_transaction, object_names, root_subintent_hash) =
225            signed_partial_transaction.resolve();
226
227        let name = name.as_ref();
228        let replaced = self.child_partial_transactions.insert(
229            name.to_string(),
230            (
231                root_subintent_hash,
232                signed_partial_transaction,
233                object_names,
234            ),
235        );
236        if replaced.is_some() {
237            panic!("Child with name {name} already exists");
238        }
239        self
240    }
241
242    /// If the intent has any children, you should call [`add_signed_child`][Self::add_signed_child] first.
243    /// These children will get added to the manifest for you, with the corresponding names.
244    ///
245    /// You may also want to try [`manifest_builder_with_lookup`][Self::manifest_builder_with_lookup].
246    pub fn manifest_builder(
247        mut self,
248        build_manifest: impl FnOnce(SubintentManifestV2Builder) -> SubintentManifestV2Builder,
249    ) -> Self {
250        let mut manifest_builder = SubintentManifestV2Builder::new_typed();
251        for (child_name, (hash, _, _)) in self.child_partial_transactions.iter() {
252            manifest_builder = manifest_builder.use_child(child_name, *hash);
253        }
254        // The manifest will be validated as part of the transaction builder validation.
255        self.root_subintent_manifest = Some(build_manifest(manifest_builder).build_no_validate());
256        self
257    }
258
259    /// If the intent has any children, you should call [`add_signed_child`][Self::add_signed_child] first.
260    /// These children will get added to the manifest for you, with the corresponding names.
261    pub fn manifest_builder_with_lookup(
262        mut self,
263        build_manifest: impl FnOnce(
264            SubintentManifestV2Builder,
265            ManifestNameLookup,
266        ) -> SubintentManifestV2Builder,
267    ) -> Self {
268        let mut manifest_builder = SubintentManifestV2Builder::new_typed();
269        let name_lookup = manifest_builder.name_lookup();
270        for (child_name, (hash, _, _)) in self.child_partial_transactions.iter() {
271            manifest_builder = manifest_builder.use_child(child_name, *hash);
272        }
273        // The manifest will be validated as part of the transaction builder validation.
274        self.root_subintent_manifest =
275            Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
276        self
277    }
278
279    /// Panics:
280    /// * If called with a manifest which references different children to those provided by [`add_signed_child`][Self::add_signed_child].
281    pub fn manifest(mut self, manifest: SubintentManifestV2) -> Self {
282        let known_subintent_hashes: IndexSet<_> = self
283            .child_partial_transactions
284            .values()
285            .map(|(hash, _, _)| ChildSubintentSpecifier { hash: *hash })
286            .collect();
287        if &manifest.children != &known_subintent_hashes {
288            panic!(
289                "The manifest's children hashes do not match those provided by `add_signed_child`"
290            );
291        }
292        self.root_subintent_manifest = Some(manifest);
293        self
294    }
295
296    pub fn message(mut self, message: MessageV2) -> Self {
297        self.root_subintent_message = Some(message);
298        self
299    }
300
301    pub fn intent_header(mut self, intent_header: IntentHeaderV2) -> Self {
302        self.root_subintent_header = Some(intent_header);
303        self
304    }
305
306    pub fn create_subintent(&mut self) -> &SubintentV2 {
307        if self.root_subintent.is_none() {
308            let (instructions, blobs, children, names) = self
309                .root_subintent_manifest
310                .take()
311                .expect("Manifest must be provided before this action is performed")
312                .for_intent_with_names();
313            let subintent = SubintentV2 {
314                intent_core: IntentCoreV2 {
315                    header: self
316                        .root_subintent_header
317                        .take()
318                        .expect("Intent Header must be provided before this action is performed"),
319                    blobs,
320                    message: self.root_subintent_message.take().unwrap_or_default(),
321                    children,
322                    instructions,
323                },
324            };
325            self.root_subintent = Some((subintent, names));
326        }
327        &self.root_subintent.as_ref().unwrap().0
328    }
329
330    pub fn create_prepared_subintent(&mut self) -> &PreparedSubintentV2 {
331        if self.prepared_root_subintent.is_none() {
332            let prepared = self
333                .create_subintent()
334                .prepare(PreparationSettings::latest_ref())
335                .expect("Expected that subintent could be prepared");
336            self.prepared_root_subintent = Some(prepared);
337        }
338        self.prepared_root_subintent.as_ref().unwrap()
339    }
340
341    pub fn subintent_hash(&mut self) -> SubintentHash {
342        self.create_prepared_subintent().subintent_hash()
343    }
344
345    pub fn sign<S: Signer>(mut self, signer: S) -> Self {
346        let hash = self.subintent_hash();
347        self.root_subintent_signatures
348            .push(IntentSignatureV1(signer.sign_with_public_key(&hash)));
349        self
350    }
351
352    pub fn multi_sign<S: Signer>(mut self, signers: impl IntoIterator<Item = S>) -> Self {
353        let hash = self.subintent_hash();
354        for signer in signers {
355            self.root_subintent_signatures
356                .push(IntentSignatureV1(signer.sign_with_public_key(&hash)));
357        }
358        self
359    }
360
361    pub fn add_signature(mut self, signature: SignatureWithPublicKeyV1) -> Self {
362        self.root_subintent_signatures
363            .push(IntentSignatureV1(signature));
364        self
365    }
366
367    fn build_internal(mut self) -> (SignedPartialTransactionV2, TransactionObjectNames) {
368        // Ensure subintent has been created
369        self.create_subintent();
370
371        // Now assemble the signed partial transaction
372        let mut aggregated_subintents = vec![];
373        let mut aggregated_subintent_signatures = vec![];
374        let mut aggregated_subintent_object_names = vec![];
375        for (_name, (_hash, child_partial_transaction, object_names)) in
376            self.child_partial_transactions
377        {
378            let SignedPartialTransactionV2 {
379                partial_transaction,
380                root_subintent_signatures: root_intent_signatures,
381                non_root_subintent_signatures: subintent_signatures,
382            } = child_partial_transaction;
383            let TransactionObjectNames {
384                root_intent: root_intent_names,
385                subintents: subintent_names,
386            } = object_names;
387            aggregated_subintents.push(partial_transaction.root_subintent);
388            aggregated_subintents.extend(partial_transaction.non_root_subintents.0);
389            aggregated_subintent_signatures.push(root_intent_signatures);
390            aggregated_subintent_signatures.extend(subintent_signatures.by_subintent);
391            aggregated_subintent_object_names.push(root_intent_names);
392            aggregated_subintent_object_names.extend(subintent_names);
393        }
394        let (root_intent, root_intent_names) = self
395            .root_subintent
396            .expect("Expected intent to already be compiled by now");
397        let signed_partial_transaction = SignedPartialTransactionV2 {
398            partial_transaction: PartialTransactionV2 {
399                root_subintent: root_intent,
400                non_root_subintents: NonRootSubintentsV2(aggregated_subintents),
401            },
402            root_subintent_signatures: IntentSignaturesV2 {
403                signatures: self.root_subintent_signatures,
404            },
405            non_root_subintent_signatures: NonRootSubintentSignaturesV2 {
406                by_subintent: aggregated_subintent_signatures,
407            },
408        };
409        let object_names = TransactionObjectNames {
410            root_intent: root_intent_names,
411            subintents: aggregated_subintent_object_names,
412        };
413        (signed_partial_transaction, object_names)
414    }
415
416    /// Builds a [`DetailedSignedPartialTransactionV2`], including the [`SignedPartialTransactionV2`]
417    /// and other useful data.
418    ///
419    /// # Panics
420    /// * Panics if any required part of the partial transaction has not been provided.
421    /// * Panics if the built transaction cannot be prepared.
422    ///
423    /// Unlike [`TransactionV2Builder`], `build()` does not validate the partial transaction, to save
424    /// lots duplicate work when building a full transaction from layers of partial transaction. If you wish,
425    /// you can opt into validation with [`build_and_validate()`][Self::build_and_validate].
426    pub fn build(self) -> DetailedSignedPartialTransactionV2 {
427        let (partial_transaction, object_names) = self.build_internal();
428        let prepared = partial_transaction
429            .prepare(PreparationSettings::latest_ref())
430            .expect("Transaction must be preparable");
431        DetailedSignedPartialTransactionV2 {
432            partial_transaction,
433            object_names,
434            root_subintent_hash: prepared.subintent_hash(),
435            non_root_subintent_hashes: prepared.non_root_subintent_hashes().collect(),
436        }
437    }
438
439    /// Builds a [`DetailedSignedPartialTransactionV2`], including the [`SignedPartialTransactionV2`]
440    /// and other useful data.
441    ///
442    /// # Panics
443    /// * Panics if any required part of the partial transaction has not been provided.
444    /// * Panics if the built transaction does not pass validation with the latest validator.
445    ///
446    /// You can use [`build()`][Self::build] to skip this validation.
447    pub fn build_and_validate(self) -> DetailedSignedPartialTransactionV2 {
448        let (partial_transaction, object_names) = self.build_internal();
449        let validator = TransactionValidator::new_with_latest_config_network_agnostic();
450        let validated = partial_transaction.prepare_and_validate(&validator)
451            .expect("Built partial transaction should be valid. Use `build()` to skip validation if needed.");
452        DetailedSignedPartialTransactionV2 {
453            partial_transaction,
454            object_names,
455            root_subintent_hash: validated.prepared.subintent_hash(),
456            non_root_subintent_hashes: validated.prepared.non_root_subintent_hashes().collect(),
457        }
458    }
459
460    /// Builds the [`SignedPartialTransactionV2`].
461    ///
462    /// You may wish to use [`build_detailed()`][Self::build_detailed] to get the hashes, or to
463    /// preserve the names used for manifest variables in the root and non-root subintents.
464    ///
465    /// Unlike [`TransactionV2Builder`], `build()` does not validate the partial transaction, to save
466    /// lots duplicate work when building a full transaction from layers of partial transaction. If you wish,
467    /// you can opt into validation with [`build_and_validate()`][Self::build_and_validate].
468    pub fn build_minimal(self) -> SignedPartialTransactionV2 {
469        self.build_internal().0
470    }
471
472    /// Builds and validates the [`SignedPartialTransactionV2`].
473    ///
474    /// You may wish to use [`build()`][Self::build] to get the hashes, or to
475    /// preserve the names used for manifest variables in the root and non-root subintents.
476    ///
477    /// # Panics
478    /// Panics if the built transaction does not pass validation with the latest validator.
479    ///
480    /// You can use [`build_minimal()`][Self::build_minimal] to skip this validation.
481    pub fn build_minimal_and_validate(self) -> SignedPartialTransactionV2 {
482        let transaction = self.build_minimal();
483        let validator = TransactionValidator::new_with_latest_config_network_agnostic();
484        transaction.prepare_and_validate(&validator)
485            .expect("Built partial transaction should be valid. Use `build()` to skip validation if needed.");
486        transaction
487    }
488}
489
490/// Includes:
491/// * A full [`SignedPartialTransactionV2`], which can be turned into a raw notarized transaction.
492/// * The [`TransactionObjectNames`], capturing the manifest names in the
493///   root subintent and non-root subintents (assuming `build_detailed` was used at all
494///   steps when building the transaction, else this detail can be lost).
495/// * The various subintenthashes of the partial transaction.
496#[derive(Debug, Clone, Eq, PartialEq)]
497pub struct DetailedSignedPartialTransactionV2 {
498    pub partial_transaction: SignedPartialTransactionV2,
499    pub object_names: TransactionObjectNames,
500    pub root_subintent_hash: SubintentHash,
501    pub non_root_subintent_hashes: IndexSet<SubintentHash>,
502}
503
504impl DetailedSignedPartialTransactionV2 {
505    pub fn to_raw(&self) -> Result<RawSignedPartialTransaction, EncodeError> {
506        self.partial_transaction.to_raw()
507    }
508}
509
510pub trait ResolvableSignedPartialTransaction {
511    fn resolve(
512        self,
513    ) -> (
514        SignedPartialTransactionV2,
515        TransactionObjectNames,
516        SubintentHash,
517    );
518}
519
520impl ResolvableSignedPartialTransaction for DetailedSignedPartialTransactionV2 {
521    fn resolve(
522        self,
523    ) -> (
524        SignedPartialTransactionV2,
525        TransactionObjectNames,
526        SubintentHash,
527    ) {
528        (
529            self.partial_transaction,
530            self.object_names,
531            self.root_subintent_hash,
532        )
533    }
534}
535
536impl ResolvableSignedPartialTransaction for SignedPartialTransactionV2 {
537    fn resolve(
538        self,
539    ) -> (
540        SignedPartialTransactionV2,
541        TransactionObjectNames,
542        SubintentHash,
543    ) {
544        let object_names = TransactionObjectNames::unknown_with_subintent_count(
545            self.non_root_subintent_signatures.by_subintent.len(),
546        );
547
548        let subintent_hash = self
549            .prepare(PreparationSettings::latest_ref())
550            .expect("Child signed partial transation could not be prepared")
551            .subintent_hash();
552
553        (self, object_names, subintent_hash)
554    }
555}
556
557/// A builder for a [`NotarizedTransactionV2`].
558///
559/// This should be used in the following order:
560///
561/// * Configure the root transaction intent:
562///   * Set the [`transaction_header`][Self::transaction_header]
563///   * Set the [`intent_header`][Self::intent_header]
564///   * Optionally, set the [`message`][Self::message]
565///   * Optionally, add one or more signed partial transaction children with [`add_signed_child`][Self::add_signed_child].
566///     These can be created with the [`PartialTransactionV2Builder`] methods [`build_with_names()`][PartialTransactionV2Builder::build_with_names] or
567///     [`build`][PartialTransactionV2Builder::build].
568///   * Set the manifest with [`manifest_builder`][Self::manifest_builder].
569/// * Sign the root transaction manifest zero or more times:
570///   * Use methods [`sign`][Self::sign] or [`multi_sign`][Self::multi_sign] [`add_signature`][Self::add_signature]
571/// * Notarize once with the notary key from the intent header:
572///   * Use either [`notarize`][Self::notarize] or [`notary_signature`][Self::notary_signature].
573/// * Build:
574///   * Use method [`build`][Self::build], [`build_no_validate`][Self::build_no_validate],
575///     [`build_minimal`][Self::build_minimal] or [`build_minimal_no_validate`][Self::build_minimal_no_validate]
576///
577/// The error messages aren't great if used out of order.
578/// In future, this may become a state-machine style builder, to catch more errors at compile time.
579#[derive(Default, Clone)]
580pub struct TransactionV2Builder {
581    // Note - these names are long, but agreed with Yulong that we would clarify
582    // non_root_subintents from root_subintent / transaction_intent so this is
583    // applying that logic to these field names
584    pub(crate) child_partial_transactions: IndexMap<
585        String,
586        (
587            SubintentHash,
588            SignedPartialTransactionV2,
589            TransactionObjectNames,
590        ),
591    >,
592    pub(crate) transaction_header: Option<TransactionHeaderV2>,
593    pub(crate) transaction_intent_header: Option<IntentHeaderV2>,
594    pub(crate) transaction_intent_message: Option<MessageV2>,
595    pub(crate) transaction_intent_manifest: Option<TransactionManifestV2>,
596    // Temporarily cached once created
597    transaction_intent_and_non_root_subintent_signatures:
598        Option<(TransactionIntentV2, NonRootSubintentSignaturesV2)>,
599    transaction_object_names: Option<TransactionObjectNames>,
600    prepared_transaction_intent: Option<PreparedTransactionIntentV2>,
601    transaction_intent_signatures: Vec<IntentSignatureV1>,
602    signed_transaction_intent: Option<SignedTransactionIntentV2>,
603    prepared_signed_transaction_intent: Option<PreparedSignedTransactionIntentV2>,
604    notary_signature: Option<NotarySignatureV2>,
605}
606
607impl TransactionV2Builder {
608    pub fn new() -> Self {
609        Default::default()
610    }
611
612    pub fn then(self, next: impl FnOnce(Self) -> Self) -> Self {
613        next(self)
614    }
615
616    /// When used with the [`manifest_builder`][Self::manifest_builder] method, the provided name and hash
617    /// are provided automatically via [`use_child`][ManifestBuilder::use_child] at the start of manifest creation.
618    ///
619    /// When used with the [`manifest`][Self::manifest] method, the provided name is simply ignored - names
620    /// are returned from the provided manifest.
621    pub fn add_signed_child(
622        mut self,
623        name: impl AsRef<str>,
624        signed_partial_transaction: impl ResolvableSignedPartialTransaction,
625    ) -> Self {
626        if self.transaction_intent_manifest.is_some() {
627            panic!("Call add_signed_child before calling manifest or manifest_builder");
628        }
629
630        let (signed_partial_transaction, object_names, root_subintent_hash) =
631            signed_partial_transaction.resolve();
632
633        let name = name.as_ref();
634        let replaced = self.child_partial_transactions.insert(
635            name.to_string(),
636            (
637                root_subintent_hash,
638                signed_partial_transaction,
639                object_names,
640            ),
641        );
642        if replaced.is_some() {
643            panic!("Child with name {name} already exists");
644        }
645        self
646    }
647
648    /// If the intent has any children, you should call [`add_signed_child`][Self::add_signed_child] first.
649    /// These children will get added to the manifest for you, with the corresponding names.
650    ///
651    /// You may also want to try [`manifest_builder_with_lookup`][Self::manifest_builder_with_lookup].
652    pub fn manifest_builder(
653        mut self,
654        build_manifest: impl FnOnce(TransactionManifestV2Builder) -> TransactionManifestV2Builder,
655    ) -> Self {
656        let mut manifest_builder = TransactionManifestV2Builder::new_typed();
657        for (child_name, (hash, _, _)) in self.child_partial_transactions.iter() {
658            manifest_builder = manifest_builder.use_child(child_name, *hash);
659        }
660        // The manifest will be validated as part of the transaction builder validation.
661        self.transaction_intent_manifest =
662            Some(build_manifest(manifest_builder).build_no_validate());
663        self
664    }
665
666    /// If the intent has any children, you should call [`add_signed_child`][Self::add_signed_child] first.
667    /// These children will get added to the manifest for you, with the corresponding names.
668    pub fn manifest_builder_with_lookup(
669        mut self,
670        build_manifest: impl FnOnce(
671            TransactionManifestV2Builder,
672            ManifestNameLookup,
673        ) -> TransactionManifestV2Builder,
674    ) -> Self {
675        let mut manifest_builder = TransactionManifestV2Builder::new_typed();
676        let name_lookup = manifest_builder.name_lookup();
677        for (child_name, (hash, _, _)) in self.child_partial_transactions.iter() {
678            manifest_builder = manifest_builder.use_child(child_name, *hash);
679        }
680        // The manifest will be validated as part of the transaction builder validation.
681        self.transaction_intent_manifest =
682            Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
683        self
684    }
685
686    /// ## Panics:
687    /// * If called with a manifest which references different children to those provided by [`add_signed_child`][Self::add_signed_child].
688    pub fn manifest(mut self, manifest: TransactionManifestV2) -> Self {
689        let known_subintent_hashes: IndexSet<_> = self
690            .child_partial_transactions
691            .values()
692            .map(|(hash, _, _)| ChildSubintentSpecifier { hash: *hash })
693            .collect();
694        if &manifest.children != &known_subintent_hashes {
695            panic!(
696                "The manifest's children hashes do not match those provided by `add_signed_child`"
697            );
698        }
699        self.transaction_intent_manifest = Some(manifest);
700        self
701    }
702
703    pub fn message(mut self, message: MessageV2) -> Self {
704        self.transaction_intent_message = Some(message);
705        self
706    }
707
708    pub fn intent_header(mut self, intent_header: IntentHeaderV2) -> Self {
709        self.transaction_intent_header = Some(intent_header);
710        self
711    }
712
713    pub fn transaction_header(mut self, transaction_header: TransactionHeaderV2) -> Self {
714        self.transaction_header = Some(transaction_header);
715        self
716    }
717
718    pub fn create_intent_and_subintent_info(&mut self) -> &TransactionIntentV2 {
719        if self
720            .transaction_intent_and_non_root_subintent_signatures
721            .is_none()
722        {
723            let manifest = self
724                .transaction_intent_manifest
725                .take()
726                .expect("Manifest must be provided before this action is performed");
727            let (instructions, blobs, child_hashes, root_object_names) =
728                manifest.for_intent_with_names();
729            let subintents = mem::take(&mut self.child_partial_transactions);
730
731            let mut aggregated_subintents = vec![];
732            let mut aggregated_subintent_signatures = vec![];
733            let mut aggregated_subintent_object_names = vec![];
734            for (_name, (_hash, child_partial_transaction, object_names)) in subintents {
735                let SignedPartialTransactionV2 {
736                    partial_transaction,
737                    root_subintent_signatures: root_intent_signatures,
738                    non_root_subintent_signatures: subintent_signatures,
739                } = child_partial_transaction;
740                let TransactionObjectNames {
741                    root_intent: root_intent_names,
742                    subintents: subintent_names,
743                } = object_names;
744                aggregated_subintents.push(partial_transaction.root_subintent);
745                aggregated_subintents.extend(partial_transaction.non_root_subintents.0);
746                aggregated_subintent_signatures.push(root_intent_signatures);
747                aggregated_subintent_signatures.extend(subintent_signatures.by_subintent);
748                aggregated_subintent_object_names.push(root_intent_names);
749                aggregated_subintent_object_names.extend(subintent_names);
750            }
751            let intent =
752                TransactionIntentV2 {
753                    transaction_header: self.transaction_header.take().expect(
754                        "Transaction Header must be provided before this action is performed",
755                    ),
756                    root_intent_core: IntentCoreV2 {
757                        header: self.transaction_intent_header.take().expect(
758                            "Intent Header must be provided before this action is performed",
759                        ),
760                        blobs,
761                        message: self.transaction_intent_message.take().unwrap_or_default(),
762                        children: child_hashes,
763                        instructions,
764                    },
765                    non_root_subintents: NonRootSubintentsV2(aggregated_subintents),
766                };
767            let subintent_signatures = NonRootSubintentSignaturesV2 {
768                by_subintent: aggregated_subintent_signatures,
769            };
770            self.transaction_intent_and_non_root_subintent_signatures =
771                Some((intent, subintent_signatures));
772            self.transaction_object_names = Some(TransactionObjectNames {
773                root_intent: root_object_names,
774                subintents: aggregated_subintent_object_names,
775            });
776        }
777        &self
778            .transaction_intent_and_non_root_subintent_signatures
779            .as_ref()
780            .unwrap()
781            .0
782    }
783
784    pub fn create_prepared_intent(&mut self) -> &PreparedTransactionIntentV2 {
785        if self.prepared_transaction_intent.is_none() {
786            let prepared = self
787                .create_intent_and_subintent_info()
788                .prepare(PreparationSettings::latest_ref())
789                .expect("Expected that the intent could be prepared");
790            self.prepared_transaction_intent = Some(prepared);
791        }
792        self.prepared_transaction_intent.as_ref().unwrap()
793    }
794
795    pub fn intent_hash(&mut self) -> TransactionIntentHash {
796        self.create_prepared_intent().transaction_intent_hash()
797    }
798
799    pub fn sign<S: Signer>(mut self, signer: S) -> Self {
800        let hash = self.intent_hash();
801        self.transaction_intent_signatures
802            .push(IntentSignatureV1(signer.sign_with_public_key(&hash)));
803        self
804    }
805
806    pub fn multi_sign<S: Signer>(mut self, signers: impl IntoIterator<Item = S>) -> Self {
807        let hash = self.intent_hash();
808        for signer in signers {
809            self.transaction_intent_signatures
810                .push(IntentSignatureV1(signer.sign_with_public_key(&hash)));
811        }
812        self
813    }
814
815    pub fn add_signature(mut self, signature: SignatureWithPublicKeyV1) -> Self {
816        self.transaction_intent_signatures
817            .push(IntentSignatureV1(signature));
818        self
819    }
820
821    pub fn create_signed_transaction_intent(&mut self) -> &SignedTransactionIntentV2 {
822        if self.signed_transaction_intent.is_none() {
823            self.create_intent_and_subintent_info();
824            let (root_intent, subintent_signatures) = self
825                .transaction_intent_and_non_root_subintent_signatures
826                .take()
827                .expect("Intent was created in create_intent_and_subintent_info()");
828            let signatures = mem::take(&mut self.transaction_intent_signatures);
829            let signed_intent = SignedTransactionIntentV2 {
830                transaction_intent: root_intent,
831                transaction_intent_signatures: IntentSignaturesV2 { signatures },
832                non_root_subintent_signatures: subintent_signatures,
833            };
834            self.signed_transaction_intent = Some(signed_intent);
835        }
836        self.signed_transaction_intent.as_ref().unwrap()
837    }
838
839    pub fn create_prepared_signed_transaction_intent(
840        &mut self,
841    ) -> &PreparedSignedTransactionIntentV2 {
842        if self.prepared_signed_transaction_intent.is_none() {
843            let prepared = self
844                .create_signed_transaction_intent()
845                .prepare(PreparationSettings::latest_ref())
846                .expect("Expected that signed intent could be prepared");
847            self.prepared_signed_transaction_intent = Some(prepared);
848        }
849        self.prepared_signed_transaction_intent.as_ref().unwrap()
850    }
851
852    pub fn notarize<S: Signer>(mut self, signer: &S) -> Self {
853        let hash = self
854            .create_prepared_signed_transaction_intent()
855            .signed_transaction_intent_hash();
856        self.notary_signature = Some(NotarySignatureV2(
857            signer.sign_with_public_key(&hash).signature(),
858        ));
859        self
860    }
861
862    pub fn notary_signature(mut self, signature: SignatureV1) -> Self {
863        self.notary_signature = Some(NotarySignatureV2(signature));
864        self
865    }
866
867    /// Creates a [`PreviewTransactionV2`]. For all non-root subintents, existing signatures
868    /// are converted into the corresponding public key.
869    ///
870    /// ## Panics
871    /// * Panics if any subintent signatures are not recoverable.
872    ///   Untrusted partial transactions should be validated before using in the transaction builder.
873    /// * If the resulting preview transaction could not be validated.
874    pub fn build_preview_transaction(
875        &mut self,
876        transaction_intent_signer_keys: impl IntoIterator<Item = PublicKey>,
877    ) -> PreviewTransactionV2 {
878        let preview_transaction =
879            self.build_preview_transaction_no_validate(transaction_intent_signer_keys);
880        let validator = TransactionValidator::new_with_latest_config_network_agnostic();
881        preview_transaction.prepare_and_validate(&validator)
882            .expect("Built preview transaction should be valid. Use `build_preview_transaction_no_validate()` to skip validation if needed.");
883        preview_transaction
884    }
885
886    /// Creates a [`PreviewTransactionV2`]. For all non-root subintents, existing signatures
887    /// are converted into the corresponding public key.
888    ///
889    /// ## Panics
890    /// * Panics if any subintent signatures are not recoverable.
891    ///   Untrusted partial transactions should be validated before using in the transaction builder.
892    pub fn build_preview_transaction_no_validate(
893        &mut self,
894        transaction_intent_signer_keys: impl IntoIterator<Item = PublicKey>,
895    ) -> PreviewTransactionV2 {
896        self.create_intent_and_subintent_info();
897        let (transaction_intent, subintent_signatures) = self
898            .transaction_intent_and_non_root_subintent_signatures
899            .clone()
900            .take()
901            .expect("Intent was created in create_intent_and_subintent_info()");
902
903        // Extract the public keys from the subintent signatures for preview purposes.
904        let non_root_subintent_signer_public_keys = subintent_signatures
905            .by_subintent
906            .into_iter()
907            .enumerate()
908            .map(|(subintent_index, sigs)| {
909                sigs.signatures
910                    .into_iter()
911                    .map(|signature| match signature.0 {
912                        SignatureWithPublicKeyV1::Secp256k1 { .. } => {
913                            let subintent = transaction_intent.non_root_subintents.0.get(subintent_index).unwrap();
914                            let subintent_hash = subintent.prepare(&PreparationSettings::latest())
915                                .expect("Untrusted partial transactions should be validated before using with the builder")
916                                .subintent_hash();
917                            verify_and_recover(subintent_hash.as_hash(), &signature.0)
918                                .expect("Signature was not valid")
919                        }
920                        SignatureWithPublicKeyV1::Ed25519 { public_key, .. } => public_key.into(),
921                    })
922                    .collect()
923            })
924            .collect();
925
926        PreviewTransactionV2 {
927            transaction_intent,
928            root_signer_public_keys: transaction_intent_signer_keys.into_iter().collect(),
929            non_root_subintent_signer_public_keys,
930        }
931    }
932
933    fn build_internal(self) -> (NotarizedTransactionV2, TransactionObjectNames) {
934        let notary_signature = self
935            .notary_signature
936            .expect("Expected notary signature to exist - ensure you call `notarize` first");
937        let transaction = NotarizedTransactionV2 {
938            signed_transaction_intent: self
939                .signed_transaction_intent
940                .expect("If the notary signature exists, the signed intent should already have been populated"),
941            notary_signature,
942        };
943        let object_names = self.transaction_object_names.expect(
944            "If the signed intent exists, the object names should have already been populated",
945        );
946        (transaction, object_names)
947    }
948
949    /// Builds a [`DetailedNotarizedTransactionV2`], including the [`NotarizedTransactionV2`]
950    /// and other useful data.
951    ///
952    /// # Panics
953    /// * If the builder has not been notarized
954    /// * If the transaction is not preparable against latest settings (e.g. it is too big)
955    pub fn build_no_validate(self) -> DetailedNotarizedTransactionV2 {
956        let (transaction, object_names) = self.build_internal();
957        let raw = transaction.to_raw().expect("Transaction must be encodable");
958        let prepared = raw
959            .prepare(PreparationSettings::latest_ref())
960            .expect("Transaction must be preparable");
961        DetailedNotarizedTransactionV2 {
962            transaction,
963            raw,
964            object_names,
965            transaction_hashes: prepared.hashes(),
966        }
967    }
968
969    /// Builds and validates a [`DetailedNotarizedTransactionV2`], which includes
970    /// a [`NotarizedTransactionV2`] and other useful data.
971    ///
972    /// # Panics
973    /// * Panics if the built transaction does not pass validation with the latest validator.
974    ///
975    /// You can use [`build_no_validate()`][Self::build_no_validate] to skip this validation.
976    pub fn build(self) -> DetailedNotarizedTransactionV2 {
977        let (transaction, object_names) = self.build_internal();
978        let validator = TransactionValidator::new_with_latest_config_network_agnostic();
979        let raw = transaction.to_raw().expect("Transaction must be encodable");
980        let validated = raw.validate_as_known_v2(&validator)
981            .expect("Built transaction should be valid. Use `build_no_validate()` to skip validation if needed.");
982        DetailedNotarizedTransactionV2 {
983            transaction,
984            raw,
985            object_names,
986            transaction_hashes: validated.prepared.hashes(),
987        }
988    }
989
990    /// Builds the [`NotarizedTransactionV2`], with no validation.
991    pub fn build_minimal_no_validate(self) -> NotarizedTransactionV2 {
992        self.build_internal().0
993    }
994
995    /// Builds and validates the [`NotarizedTransactionV2`].
996    ///
997    /// You may prefer [`build()`][Self::build] instead if you need the transaction hashes,
998    /// or wish to keep a record of the names used in the manifest variables.
999    ///
1000    /// # Panics
1001    /// Panics if the built transaction does not pass validation with the latest validator.
1002    ///
1003    /// You can use [`build_minimal_no_validate()`][Self::build_minimal_no_validate] to skip this validation.
1004    pub fn build_minimal(self) -> NotarizedTransactionV2 {
1005        let transaction = self.build_minimal_no_validate();
1006        let validator = TransactionValidator::new_with_latest_config_network_agnostic();
1007        transaction.prepare_and_validate(&validator)
1008            .expect("Built transaction should be valid. Use `build_no_validate()` to skip validation if needed.");
1009        transaction
1010    }
1011}
1012
1013/// Includes:
1014/// * A full [`NotarizedTransactionV2`], which can be turned into a raw notarized transaction.
1015/// * The [`TransactionObjectNames`], capturing the manifest names in the
1016///   root subintent and non-root subintents (assuming `build_detailed` was used at all
1017///   steps when building the transaction, else this detail can be lost).
1018/// * The various hashes of the transaction.
1019#[derive(Debug, Clone, Eq, PartialEq)]
1020pub struct DetailedNotarizedTransactionV2 {
1021    pub transaction: NotarizedTransactionV2,
1022    pub raw: RawNotarizedTransaction,
1023    pub object_names: TransactionObjectNames,
1024    pub transaction_hashes: UserTransactionHashes,
1025}
1026
1027impl IntoExecutable for DetailedNotarizedTransactionV2 {
1028    type Error = TransactionValidationError;
1029
1030    fn into_executable(
1031        self,
1032        validator: &TransactionValidator,
1033    ) -> Result<ExecutableTransaction, Self::Error> {
1034        self.raw.into_executable(validator)
1035    }
1036}
1037
1038impl AsRef<RawNotarizedTransaction> for DetailedNotarizedTransactionV2 {
1039    fn as_ref(&self) -> &RawNotarizedTransaction {
1040        &self.raw
1041    }
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046    use radix_common::network::NetworkDefinition;
1047    use radix_common::types::Epoch;
1048
1049    use super::*;
1050    use crate::builder::*;
1051    use crate::internal_prelude::Secp256k1PrivateKey;
1052
1053    #[test]
1054    #[allow(deprecated)]
1055    fn notary_as_signatory() {
1056        let private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
1057
1058        let transaction = TransactionBuilder::new()
1059            .header(TransactionHeaderV1 {
1060                network_id: NetworkDefinition::simulator().id,
1061                start_epoch_inclusive: Epoch::zero(),
1062                end_epoch_exclusive: Epoch::of(100),
1063                nonce: 5,
1064                notary_public_key: private_key.public_key().into(),
1065                notary_is_signatory: true,
1066                tip_percentage: 5,
1067            })
1068            .manifest(ManifestBuilder::new().drop_auth_zone_proofs().build())
1069            .notarize(&private_key)
1070            .build();
1071
1072        let prepared = transaction
1073            .prepare(PreparationSettings::latest_ref())
1074            .unwrap();
1075        assert_eq!(
1076            prepared
1077                .signed_intent
1078                .intent
1079                .header
1080                .inner
1081                .notary_is_signatory,
1082            true
1083        );
1084    }
1085}