1use crate::internal_prelude::*;
2use crate::model::*;
3use crate::signing::Signer;
4
5pub type TransactionBuilder = TransactionV1Builder;
20
21impl TransactionBuilder {
22 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
165pub type SignedPartialTransactionV2Builder = PartialTransactionV2Builder;
167
168#[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 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 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 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 self.root_subintent_manifest = Some(build_manifest(manifest_builder).build_no_validate());
260 self
261 }
262
263 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 self.root_subintent_manifest =
279 Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
280 self
281 }
282
283 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 self.create_subintent();
374
375 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 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 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 pub fn build_minimal(self) -> SignedPartialTransactionV2 {
473 self.build_internal().0
474 }
475
476 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#[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#[derive(Default, Clone)]
584pub struct TransactionV2Builder {
585 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 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 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 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 self.transaction_intent_manifest =
666 Some(build_manifest(manifest_builder).build_no_validate());
667 self
668 }
669
670 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 self.transaction_intent_manifest =
686 Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
687 self
688 }
689
690 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 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 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 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 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 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 pub fn build_minimal_no_validate(self) -> NotarizedTransactionV2 {
995 self.build_internal().0
996 }
997
998 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#[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}