1use super::*;
2use crate::internal_prelude::*;
3
4#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoDescribe)]
5pub enum LedgerTransaction {
6 #[sbor(discriminator(GENESIS_LEDGER_TRANSACTION_DISCRIMINATOR))]
7 Genesis(Box<GenesisTransaction>),
8 #[sbor(discriminator(USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR))]
9 UserV1(Box<NotarizedTransactionV1>),
10 #[sbor(discriminator(ROUND_UPDATE_V1_LEDGER_TRANSACTION_DISCRIMINATOR))]
11 RoundUpdateV1(Box<RoundUpdateTransactionV1>),
12 #[sbor(discriminator(FLASH_V1_LEDGER_TRANSACTION_DISCRIMINATOR))]
13 FlashV1(Box<FlashTransactionV1>),
14 #[sbor(discriminator(USER_V2_LEDGER_TRANSACTION_DISCRIMINATOR))]
15 UserV2(Box<NotarizedTransactionV2>),
16}
17
18const GENESIS_LEDGER_TRANSACTION_DISCRIMINATOR: u8 = 0;
19const USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR: u8 = 1;
20const ROUND_UPDATE_V1_LEDGER_TRANSACTION_DISCRIMINATOR: u8 = 2;
21const FLASH_V1_LEDGER_TRANSACTION_DISCRIMINATOR: u8 = 3;
22const USER_V2_LEDGER_TRANSACTION_DISCRIMINATOR: u8 = 4;
23
24enum LedgerTransactionKind {
25 Genesis,
26 User,
27 Validator,
28 ProtocolUpdate,
29}
30
31impl LedgerTransactionKind {
32 fn discriminator_for_hash(&self) -> u8 {
33 match self {
34 LedgerTransactionKind::Genesis => GENESIS_LEDGER_TRANSACTION_DISCRIMINATOR,
35 LedgerTransactionKind::User => USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR,
36 LedgerTransactionKind::Validator => ROUND_UPDATE_V1_LEDGER_TRANSACTION_DISCRIMINATOR,
37 LedgerTransactionKind::ProtocolUpdate => FLASH_V1_LEDGER_TRANSACTION_DISCRIMINATOR,
38 }
39 }
40}
41
42define_raw_transaction_payload!(
43 RawLedgerTransaction,
44 TransactionPayloadKind::LedgerTransaction
45);
46
47impl RawLedgerTransaction {
48 pub fn prepare(
49 &self,
50 settings: &PreparationSettings,
51 ) -> Result<PreparedLedgerTransaction, PrepareError> {
52 PreparedLedgerTransaction::prepare(self, settings)
53 }
54
55 pub fn validate(
56 &self,
57 validator: &TransactionValidator,
58 accepted_kind: AcceptedLedgerTransactionKind,
59 ) -> Result<ValidatedLedgerTransaction, LedgerTransactionValidationError> {
60 let prepared = PreparedLedgerTransaction::prepare(self, validator.preparation_settings())?;
61 prepared.validate(validator, accepted_kind)
62 }
63
64 pub fn create_executable(
65 &self,
66 validator: &TransactionValidator,
67 accepted_kind: AcceptedLedgerTransactionKind,
68 ) -> Result<ExecutableTransaction, LedgerTransactionValidationError> {
69 let validated = self.validate(validator, accepted_kind)?;
70 validated.create_executable()
71 }
72
73 pub fn create_identifiable_ledger_executable(
74 &self,
75 validator: &TransactionValidator,
76 accepted_kind: AcceptedLedgerTransactionKind,
77 ) -> Result<IdentifiedLedgerExecutable, LedgerTransactionValidationError> {
78 let validated = self.validate(validator, accepted_kind)?;
79 let hashes = validated.create_hashes();
80 let executable = validated.create_ledger_executable();
81 Ok(IdentifiedLedgerExecutable { executable, hashes })
82 }
83}
84
85impl IntoExecutable for RawLedgerTransaction {
86 type Error = LedgerTransactionValidationError;
87
88 fn into_executable(
89 self,
90 validator: &TransactionValidator,
91 ) -> Result<ExecutableTransaction, Self::Error> {
92 self.create_executable(validator, AcceptedLedgerTransactionKind::Any)
93 }
94}
95
96#[derive(Debug, Clone)]
97pub enum LedgerTransactionValidationError {
98 ValidationError(TransactionValidationError),
99 GenesisTransactionNotCurrentlyPermitted,
100 UserTransactionNotCurrentlyPermitted,
101 ValidateTransactionNotCurrentlyPermitted,
102 ProtocolUpdateNotCurrentlyPermitted,
103 FlashNotCurrentlyPermitted,
104}
105
106impl From<TransactionValidationError> for LedgerTransactionValidationError {
107 fn from(value: TransactionValidationError) -> Self {
108 Self::ValidationError(value)
109 }
110}
111
112impl From<PrepareError> for LedgerTransactionValidationError {
113 fn from(value: PrepareError) -> Self {
114 Self::ValidationError(value.into())
115 }
116}
117
118impl TransactionPayload for LedgerTransaction {
119 type Prepared = PreparedLedgerTransaction;
120 type Raw = RawLedgerTransaction;
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoDescribe)]
124pub enum GenesisTransaction {
125 #[sbor(discriminator(GENESIS_TRANSACTION_FLASH_DISCRIMINATOR))]
126 Flash,
127 #[sbor(discriminator(GENESIS_TRANSACTION_SYSTEM_TRANSACTION_DISCRIMINATOR))]
128 Transaction(Box<SystemTransactionV1>),
129}
130
131const GENESIS_TRANSACTION_FLASH_DISCRIMINATOR: u8 = 0;
132const GENESIS_TRANSACTION_SYSTEM_TRANSACTION_DISCRIMINATOR: u8 = 1;
133
134pub struct PreparedLedgerTransaction {
135 pub inner: PreparedLedgerTransactionInner,
136 pub summary: Summary,
137}
138
139impl PreparedLedgerTransaction {
140 pub fn into_user(self) -> Option<PreparedUserTransaction> {
141 match self.inner {
142 PreparedLedgerTransactionInner::User(t) => Some(t),
143 _ => None,
144 }
145 }
146
147 pub fn as_user(&self) -> Option<&PreparedUserTransaction> {
148 match &self.inner {
149 PreparedLedgerTransactionInner::User(t) => Some(t),
150 _ => None,
151 }
152 }
153
154 pub fn create_hashes(&self) -> LedgerTransactionHashes {
155 LedgerTransactionHashes {
156 ledger_transaction_hash: self.ledger_transaction_hash(),
157 kinded: match &self.inner {
158 PreparedLedgerTransactionInner::Genesis(t) => KindedTransactionHashes::Genesis {
159 system_transaction_hash: t.system_transaction_hash(),
160 },
161 PreparedLedgerTransactionInner::User(t) => {
162 KindedTransactionHashes::User(t.hashes())
163 }
164 PreparedLedgerTransactionInner::Validator(t) => {
165 KindedTransactionHashes::RoundUpdateV1 {
166 round_update_hash: t.round_update_transaction_hash(),
167 }
168 }
169 PreparedLedgerTransactionInner::ProtocolUpdate(t) => {
170 KindedTransactionHashes::FlashV1 {
171 flash_transaction_hash: t.flash_transaction_hash(),
172 }
173 }
174 },
175 }
176 }
177
178 pub fn validate(
179 self,
180 validator: &TransactionValidator,
181 accepted_kind: AcceptedLedgerTransactionKind,
182 ) -> Result<ValidatedLedgerTransaction, LedgerTransactionValidationError> {
183 let validated_inner = match self.inner {
184 PreparedLedgerTransactionInner::Genesis(t) => {
185 if !accepted_kind.permits_genesis() {
186 return Err(
187 LedgerTransactionValidationError::GenesisTransactionNotCurrentlyPermitted,
188 );
189 }
190 ValidatedLedgerTransactionInner::Genesis(t)
191 }
192 PreparedLedgerTransactionInner::User(t) => {
193 if !accepted_kind.permits_user() {
194 return Err(
195 LedgerTransactionValidationError::UserTransactionNotCurrentlyPermitted,
196 );
197 }
198 ValidatedLedgerTransactionInner::User(t.validate(validator)?)
199 }
200 PreparedLedgerTransactionInner::Validator(t) => {
201 if !accepted_kind.permits_validator() {
202 return Err(
203 LedgerTransactionValidationError::ValidateTransactionNotCurrentlyPermitted,
204 );
205 }
206 ValidatedLedgerTransactionInner::Validator(t)
207 }
208 PreparedLedgerTransactionInner::ProtocolUpdate(t) => {
209 if !accepted_kind.permits_protocol_update() {
210 return Err(
211 LedgerTransactionValidationError::ProtocolUpdateNotCurrentlyPermitted,
212 );
213 }
214 ValidatedLedgerTransactionInner::ProtocolUpdate(t)
215 }
216 };
217 Ok(ValidatedLedgerTransaction {
218 inner: validated_inner,
219 summary: self.summary,
220 })
221 }
222}
223
224#[derive(Debug, Copy, Clone)]
225pub enum AcceptedLedgerTransactionKind {
226 Any,
227 UserOnly,
228 GenesisOnly,
229 ValidatorOnly,
230 ProtocolUpdateOnly,
231 UserOrValidator,
232}
233
234impl AcceptedLedgerTransactionKind {
235 fn permits_genesis(&self) -> bool {
236 match self {
237 AcceptedLedgerTransactionKind::Any => true,
238 AcceptedLedgerTransactionKind::UserOnly => false,
239 AcceptedLedgerTransactionKind::GenesisOnly => true,
240 AcceptedLedgerTransactionKind::ValidatorOnly => false,
241 AcceptedLedgerTransactionKind::ProtocolUpdateOnly => false,
242 AcceptedLedgerTransactionKind::UserOrValidator => false,
243 }
244 }
245
246 fn permits_user(&self) -> bool {
247 match self {
248 AcceptedLedgerTransactionKind::Any => true,
249 AcceptedLedgerTransactionKind::UserOnly => true,
250 AcceptedLedgerTransactionKind::GenesisOnly => false,
251 AcceptedLedgerTransactionKind::ValidatorOnly => false,
252 AcceptedLedgerTransactionKind::ProtocolUpdateOnly => false,
253 AcceptedLedgerTransactionKind::UserOrValidator => true,
254 }
255 }
256
257 fn permits_validator(&self) -> bool {
258 match self {
259 AcceptedLedgerTransactionKind::Any => true,
260 AcceptedLedgerTransactionKind::UserOnly => false,
261 AcceptedLedgerTransactionKind::GenesisOnly => false,
262 AcceptedLedgerTransactionKind::ValidatorOnly => true,
263 AcceptedLedgerTransactionKind::ProtocolUpdateOnly => false,
264 AcceptedLedgerTransactionKind::UserOrValidator => true,
265 }
266 }
267
268 fn permits_protocol_update(&self) -> bool {
269 match self {
270 AcceptedLedgerTransactionKind::Any => true,
271 AcceptedLedgerTransactionKind::UserOnly => false,
272 AcceptedLedgerTransactionKind::GenesisOnly => false,
273 AcceptedLedgerTransactionKind::ValidatorOnly => false,
274 AcceptedLedgerTransactionKind::ProtocolUpdateOnly => true,
275 AcceptedLedgerTransactionKind::UserOrValidator => false,
276 }
277 }
278}
279
280impl_has_summary!(PreparedLedgerTransaction);
281
282#[allow(clippy::large_enum_variant)]
283pub enum PreparedLedgerTransactionInner {
284 Genesis(PreparedGenesisTransaction),
285 User(PreparedUserTransaction),
286 Validator(PreparedRoundUpdateTransactionV1),
287 ProtocolUpdate(PreparedFlashTransactionV1),
288}
289
290impl PreparedLedgerTransactionInner {
291 fn get_kind(&self) -> LedgerTransactionKind {
292 match self {
293 Self::Genesis(_) => LedgerTransactionKind::Genesis,
294 Self::User(_) => LedgerTransactionKind::User,
295 Self::Validator(_) => LedgerTransactionKind::Validator,
296 Self::ProtocolUpdate(_) => LedgerTransactionKind::ProtocolUpdate,
297 }
298 }
299
300 pub fn get_ledger_hash(&self) -> LedgerTransactionHash {
301 LedgerTransactionHash::for_kind(self.get_kind(), &self.get_summary().hash)
302 }
303}
304
305impl HasSummary for PreparedLedgerTransactionInner {
306 fn get_summary(&self) -> &Summary {
307 match self {
308 Self::Genesis(t) => t.get_summary(),
309 Self::User(t) => t.get_summary(),
310 Self::Validator(t) => t.get_summary(),
311 Self::ProtocolUpdate(t) => t.get_summary(),
312 }
313 }
314
315 fn summary_mut(&mut self) -> &mut Summary {
316 match self {
317 Self::Genesis(t) => t.summary_mut(),
318 Self::User(t) => t.summary_mut(),
319 Self::Validator(t) => t.summary_mut(),
320 Self::ProtocolUpdate(t) => t.summary_mut(),
321 }
322 }
323}
324
325impl TransactionPreparableFromValue for PreparedLedgerTransactionInner {
326 fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
327 decoder.track_stack_depth_increase()?;
328 let (discriminator, length) = decoder.read_enum_header()?;
329 let prepared_inner = match discriminator {
330 GENESIS_LEDGER_TRANSACTION_DISCRIMINATOR => {
331 check_length(length, 1)?;
332 let (discriminator, length) = decoder.read_enum_header()?;
333 let genesis_transaction = match discriminator {
334 GENESIS_TRANSACTION_FLASH_DISCRIMINATOR => {
335 check_length(length, 0)?;
336 PreparedGenesisTransaction::Flash(Summary {
337 effective_length: 0,
338 total_bytes_hashed: 0,
339 hash: hash("Genesis Flash"),
340 })
341 }
342 GENESIS_TRANSACTION_SYSTEM_TRANSACTION_DISCRIMINATOR => {
343 check_length(length, 1)?;
344 let prepared = PreparedSystemTransactionV1::prepare_from_value(decoder)?;
345 PreparedGenesisTransaction::Transaction(prepared)
346 }
347 _ => return Err(unknown_discriminator(discriminator)),
348 };
349 PreparedLedgerTransactionInner::Genesis(genesis_transaction)
350 }
351 USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
352 check_length(length, 1)?;
353 let prepared = PreparedNotarizedTransactionV1::prepare_from_value(decoder)?;
354 PreparedLedgerTransactionInner::User(PreparedUserTransaction::V1(prepared))
355 }
356 ROUND_UPDATE_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
357 check_length(length, 1)?;
358 let prepared = PreparedRoundUpdateTransactionV1::prepare_from_value(decoder)?;
359 PreparedLedgerTransactionInner::Validator(prepared)
360 }
361 FLASH_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
362 check_length(length, 1)?;
363 let prepared = PreparedFlashTransactionV1::prepare_from_value(decoder)?;
364 PreparedLedgerTransactionInner::ProtocolUpdate(prepared)
365 }
366 USER_V2_LEDGER_TRANSACTION_DISCRIMINATOR => {
367 check_length(length, 1)?;
368 let prepared = PreparedNotarizedTransactionV2::prepare_from_value(decoder)?;
369 PreparedLedgerTransactionInner::User(PreparedUserTransaction::V2(prepared))
370 }
371 _ => return Err(unknown_discriminator(discriminator)),
372 };
373 decoder.track_stack_depth_decrease()?;
374
375 Ok(prepared_inner)
376 }
377}
378
379fn check_length(actual: usize, expected: usize) -> Result<(), PrepareError> {
380 if actual != expected {
381 return Err(PrepareError::DecodeError(DecodeError::UnexpectedSize {
382 expected,
383 actual,
384 }));
385 }
386 Ok(())
387}
388
389fn unknown_discriminator(discriminator: u8) -> PrepareError {
390 PrepareError::DecodeError(DecodeError::UnknownDiscriminator(discriminator))
391}
392
393#[allow(clippy::large_enum_variant)]
394pub enum PreparedGenesisTransaction {
395 Flash(Summary),
396 Transaction(PreparedSystemTransactionV1),
397}
398
399impl HasSummary for PreparedGenesisTransaction {
400 fn get_summary(&self) -> &Summary {
401 match self {
402 PreparedGenesisTransaction::Flash(summary) => summary,
403 PreparedGenesisTransaction::Transaction(t) => t.get_summary(),
404 }
405 }
406
407 fn summary_mut(&mut self) -> &mut Summary {
408 match self {
409 PreparedGenesisTransaction::Flash(summary) => summary,
410 PreparedGenesisTransaction::Transaction(t) => t.summary_mut(),
411 }
412 }
413}
414
415impl HasSystemTransactionHash for PreparedGenesisTransaction {
416 fn system_transaction_hash(&self) -> SystemTransactionHash {
417 match self {
418 PreparedGenesisTransaction::Flash(summary) => SystemTransactionHash(summary.hash),
419 PreparedGenesisTransaction::Transaction(transaction) => {
420 transaction.system_transaction_hash()
421 }
422 }
423 }
424}
425
426impl PreparedTransaction for PreparedLedgerTransaction {
427 type Raw = RawLedgerTransaction;
428
429 fn prepare_from_transaction_enum(
430 decoder: &mut TransactionDecoder,
431 ) -> Result<Self, PrepareError> {
432 decoder.track_stack_depth_increase()?;
433 decoder.read_header(
434 ExpectedTupleHeader::EnumWithValueKind {
435 discriminator: TransactionDiscriminator::Ledger as u8,
436 },
437 1,
438 )?;
439 let inner = PreparedLedgerTransactionInner::prepare_from_value(decoder)?;
440 decoder.track_stack_depth_decrease()?;
441
442 let summary = Summary {
443 effective_length: inner.get_summary().effective_length,
444 total_bytes_hashed: inner.get_summary().total_bytes_hashed,
445 hash: inner.get_ledger_hash().0,
446 };
447 Ok(Self { inner, summary })
448 }
449}
450
451impl IntoExecutable for PreparedLedgerTransaction {
452 type Error = LedgerTransactionValidationError;
453
454 fn into_executable(
455 self,
456 validator: &TransactionValidator,
457 ) -> Result<ExecutableTransaction, Self::Error> {
458 self.validate(validator, AcceptedLedgerTransactionKind::Any)?
459 .into_executable(validator)
460 }
461}
462
463pub struct ValidatedLedgerTransaction {
464 pub inner: ValidatedLedgerTransactionInner,
465 pub summary: Summary,
466}
467
468#[allow(clippy::large_enum_variant)]
469pub enum ValidatedLedgerTransactionInner {
470 Genesis(PreparedGenesisTransaction),
471 User(ValidatedUserTransaction),
472 Validator(PreparedRoundUpdateTransactionV1),
473 ProtocolUpdate(PreparedFlashTransactionV1),
474}
475
476impl ValidatedLedgerTransaction {
477 pub fn intent_hash_if_user(&self) -> Option<TransactionIntentHash> {
478 match &self.inner {
479 ValidatedLedgerTransactionInner::Genesis(_) => None,
480 ValidatedLedgerTransactionInner::User(t) => Some(t.transaction_intent_hash()),
481 ValidatedLedgerTransactionInner::Validator(_) => None,
482 ValidatedLedgerTransactionInner::ProtocolUpdate(_) => None,
483 }
484 }
485
486 pub fn create_ledger_executable(self) -> LedgerExecutable {
487 match self.inner {
488 ValidatedLedgerTransactionInner::Genesis(genesis) => match genesis {
489 PreparedGenesisTransaction::Flash(_) => LedgerExecutable::GenesisFlash,
490 PreparedGenesisTransaction::Transaction(t) => LedgerExecutable::Transaction {
491 executable: t
492 .create_executable(btreeset!(system_execution(SystemExecution::Protocol))),
493 },
494 },
495 ValidatedLedgerTransactionInner::User(t) => LedgerExecutable::Transaction {
496 executable: t.create_executable(),
497 },
498 ValidatedLedgerTransactionInner::Validator(t) => LedgerExecutable::Transaction {
499 executable: t.create_executable(),
500 },
501 ValidatedLedgerTransactionInner::ProtocolUpdate(t) => LedgerExecutable::Flash {
502 updates: t.state_updates,
503 },
504 }
505 }
506
507 pub fn create_executable(
509 self,
510 ) -> Result<ExecutableTransaction, LedgerTransactionValidationError> {
511 match self.create_ledger_executable() {
512 LedgerExecutable::GenesisFlash | LedgerExecutable::Flash { .. } => {
513 Err(LedgerTransactionValidationError::FlashNotCurrentlyPermitted)
514 }
515 LedgerExecutable::Transaction { executable } => Ok(executable),
516 }
517 }
518
519 pub fn create_hashes(&self) -> LedgerTransactionHashes {
520 LedgerTransactionHashes {
521 ledger_transaction_hash: self.ledger_transaction_hash(),
522 kinded: match &self.inner {
523 ValidatedLedgerTransactionInner::Genesis(t) => KindedTransactionHashes::Genesis {
524 system_transaction_hash: t.system_transaction_hash(),
525 },
526 ValidatedLedgerTransactionInner::User(t) => {
527 KindedTransactionHashes::User(t.hashes())
528 }
529 ValidatedLedgerTransactionInner::Validator(t) => {
530 KindedTransactionHashes::RoundUpdateV1 {
531 round_update_hash: t.round_update_transaction_hash(),
532 }
533 }
534 ValidatedLedgerTransactionInner::ProtocolUpdate(t) => {
535 KindedTransactionHashes::FlashV1 {
536 flash_transaction_hash: t.flash_transaction_hash(),
537 }
538 }
539 },
540 }
541 }
542}
543
544#[derive(Debug, Clone, PartialEq, Eq)]
545pub struct IdentifiedLedgerExecutable {
546 pub executable: LedgerExecutable,
547 pub hashes: LedgerTransactionHashes,
548}
549
550#[allow(clippy::large_enum_variant)]
551#[derive(Debug, Clone, PartialEq, Eq)]
552pub enum LedgerExecutable {
553 GenesisFlash,
555 Flash {
556 updates: StateUpdates,
559 },
560 Transaction {
561 executable: ExecutableTransaction,
562 },
563}
564
565impl IntoExecutable for ValidatedLedgerTransaction {
566 type Error = LedgerTransactionValidationError;
567
568 fn into_executable(
569 self,
570 _validator: &TransactionValidator,
571 ) -> Result<ExecutableTransaction, Self::Error> {
572 self.create_executable()
573 }
574}
575
576define_versioned! {
577 #[derive(Debug, Clone, ScryptoSbor)]
581 pub VersionedLedgerTransactionHashes(LedgerTransactionHashesVersions) {
582 previous_versions: [
583 1 => LedgerTransactionHashesV1: { updates_to: 2 },
584 ],
585 latest_version: {
586 2 => LedgerTransactionHashes = LedgerTransactionHashesV2,
587 },
588 },
589 outer_attributes: [
590 #[derive(ScryptoSborAssertion)]
591 #[sbor_assert(backwards_compatible(
592 bottlenose = "FILE:ledger_transaction_hashes_bottlenose.bin",
593 cuttlefish = "FILE:ledger_transaction_hashes_cuttlefish.bin"
594 ))]
595 ]
596}
597
598#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
599pub struct LedgerTransactionHashesV2 {
600 pub ledger_transaction_hash: LedgerTransactionHash,
601 pub kinded: KindedTransactionHashesV2,
602}
603
604#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
605pub struct LedgerTransactionHashesV1 {
606 pub ledger_transaction_hash: LedgerTransactionHash,
607 pub kinded: KindedTransactionHashesV1,
608}
609
610impl From<LedgerTransactionHashesV1> for LedgerTransactionHashesV2 {
611 fn from(value: LedgerTransactionHashesV1) -> Self {
612 let LedgerTransactionHashesV1 {
613 ledger_transaction_hash,
614 kinded,
615 } = value;
616 LedgerTransactionHashesV2 {
617 ledger_transaction_hash,
618 kinded: kinded.into(),
619 }
620 }
621}
622
623impl LedgerTransactionHashes {
624 pub fn as_user(&self) -> Option<&UserTransactionHashes> {
625 self.kinded.as_user()
626 }
627}
628
629pub type KindedTransactionHashes = KindedTransactionHashesV2;
630
631impl From<KindedTransactionHashesV1> for KindedTransactionHashesV2 {
632 fn from(value: KindedTransactionHashesV1) -> Self {
633 match value {
634 KindedTransactionHashesV1::Genesis {
635 system_transaction_hash,
636 } => KindedTransactionHashesV2::Genesis {
637 system_transaction_hash,
638 },
639 KindedTransactionHashesV1::User(user_transaction_hashes_v1) => {
640 KindedTransactionHashesV2::User(user_transaction_hashes_v1.into())
641 }
642 KindedTransactionHashesV1::RoundUpdateV1 { round_update_hash } => {
643 KindedTransactionHashesV2::RoundUpdateV1 { round_update_hash }
644 }
645 KindedTransactionHashesV1::FlashV1 {
646 flash_transaction_hash,
647 } => KindedTransactionHashesV2::FlashV1 {
648 flash_transaction_hash,
649 },
650 }
651 }
652}
653
654#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
655pub enum KindedTransactionHashesV1 {
656 Genesis {
657 system_transaction_hash: SystemTransactionHash,
658 },
659 User(#[sbor(flatten)] UserTransactionHashesV1),
660 RoundUpdateV1 {
661 round_update_hash: RoundUpdateTransactionHash,
662 },
663 FlashV1 {
664 flash_transaction_hash: FlashTransactionHash,
665 },
666}
667
668#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
669pub enum KindedTransactionHashesV2 {
670 Genesis {
671 system_transaction_hash: SystemTransactionHash,
672 },
673 User(#[sbor(flatten)] UserTransactionHashesV2),
674 RoundUpdateV1 {
675 round_update_hash: RoundUpdateTransactionHash,
676 },
677 FlashV1 {
678 flash_transaction_hash: FlashTransactionHash,
679 },
680}
681
682impl KindedTransactionHashes {
683 pub fn as_user(&self) -> Option<&UserTransactionHashes> {
684 match self {
685 KindedTransactionHashes::User(user) => Some(user),
686 _ => None,
687 }
688 }
689}
690
691impl HasLedgerTransactionHash for ValidatedLedgerTransaction {
692 fn ledger_transaction_hash(&self) -> LedgerTransactionHash {
693 LedgerTransactionHash::from_hash(self.summary.hash)
694 }
695}
696
697define_wrapped_hash!(LedgerTransactionHash);
698
699impl LedgerTransactionHash {
700 pub fn for_genesis(hash: &SystemTransactionHash) -> Self {
701 Self::for_kind(LedgerTransactionKind::Genesis, &hash.0)
702 }
703
704 pub fn for_user(hash: &NotarizedTransactionHash) -> Self {
705 Self::for_kind(LedgerTransactionKind::User, &hash.0)
706 }
707
708 pub fn for_round_update(hash: &RoundUpdateTransactionHash) -> Self {
709 Self::for_kind(LedgerTransactionKind::Validator, &hash.0)
710 }
711
712 fn for_kind(kind: LedgerTransactionKind, inner: &Hash) -> Self {
713 Self(
714 HashAccumulator::new()
715 .concat([
716 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
717 TransactionDiscriminator::Ledger as u8,
718 kind.discriminator_for_hash(),
719 ])
720 .concat(inner.as_slice())
721 .finalize(),
722 )
723 }
724}
725
726impl IsTransactionHashWithStaticHrp for LedgerTransactionHash {
727 fn static_hrp(hrp_set: &HrpSet) -> &str {
728 &hrp_set.ledger_transaction
729 }
730}
731
732pub trait HasLedgerTransactionHash {
733 fn ledger_transaction_hash(&self) -> LedgerTransactionHash;
734}
735
736impl HasLedgerTransactionHash for PreparedLedgerTransaction {
737 fn ledger_transaction_hash(&self) -> LedgerTransactionHash {
738 LedgerTransactionHash::from_hash(self.summary.hash)
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 pub fn v1_ledger_transaction_structure() {
748 let sig_1_private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
749 let sig_2_private_key = Ed25519PrivateKey::from_u64(2).unwrap();
750 let notary_private_key = Ed25519PrivateKey::from_u64(3).unwrap();
751
752 let notarized = TransactionBuilder::new()
753 .header(TransactionHeaderV1 {
754 network_id: 21,
755 start_epoch_inclusive: Epoch::of(0),
756 end_epoch_exclusive: Epoch::of(100),
757 nonce: 0,
758 notary_public_key: notary_private_key.public_key().into(),
759 notary_is_signatory: true,
760 tip_percentage: 0,
761 })
762 .manifest(ManifestBuilder::new().drop_all_proofs().build())
763 .sign(&sig_1_private_key)
764 .sign(&sig_2_private_key)
765 .notarize(¬ary_private_key)
766 .build();
767
768 let prepared_notarized = notarized
769 .prepare(PreparationSettings::latest_ref())
770 .expect("Notarized can be prepared");
771
772 let ledger = LedgerTransaction::UserV1(Box::new(notarized));
773 let raw_ledger_transaction = ledger.to_raw().expect("Can be encoded");
774 LedgerTransaction::from_raw(&raw_ledger_transaction).expect("Can be decoded");
775 let prepared_ledger_transaction = raw_ledger_transaction
776 .prepare(PreparationSettings::latest_ref())
777 .expect("Can be prepared");
778
779 let expected_intent_hash = LedgerTransactionHash::from_hash(hash(
780 [
781 [
782 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
783 TransactionDiscriminator::Ledger as u8,
784 USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR,
785 ]
786 .as_slice(),
787 prepared_notarized.notarized_transaction_hash().0.as_slice(),
788 ]
789 .concat(),
790 ));
791 assert_eq!(
792 prepared_ledger_transaction.ledger_transaction_hash(),
793 expected_intent_hash
794 );
795 assert_eq!(
796 LedgerTransactionHash::for_user(&prepared_notarized.notarized_transaction_hash()),
797 expected_intent_hash
798 );
799 }
800}