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