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 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
161pub type SignedPartialTransactionV2Builder = PartialTransactionV2Builder;
163
164#[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 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 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 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 self.root_subintent_manifest = Some(build_manifest(manifest_builder).build_no_validate());
256 self
257 }
258
259 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 self.root_subintent_manifest =
275 Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
276 self
277 }
278
279 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 self.create_subintent();
370
371 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 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 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 pub fn build_minimal(self) -> SignedPartialTransactionV2 {
469 self.build_internal().0
470 }
471
472 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#[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#[derive(Default, Clone)]
580pub struct TransactionV2Builder {
581 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 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 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 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 self.transaction_intent_manifest =
662 Some(build_manifest(manifest_builder).build_no_validate());
663 self
664 }
665
666 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 self.transaction_intent_manifest =
682 Some(build_manifest(manifest_builder, name_lookup).build_no_validate());
683 self
684 }
685
686 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 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 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 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 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 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 pub fn build_minimal_no_validate(self) -> NotarizedTransactionV2 {
992 self.build_internal().0
993 }
994
995 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#[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}