radix_transactions/model/
ledger_transaction.rs

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
282pub enum PreparedLedgerTransactionInner {
283    Genesis(PreparedGenesisTransaction),
284    User(PreparedUserTransaction),
285    Validator(PreparedRoundUpdateTransactionV1),
286    ProtocolUpdate(PreparedFlashTransactionV1),
287}
288
289impl PreparedLedgerTransactionInner {
290    fn get_kind(&self) -> LedgerTransactionKind {
291        match self {
292            Self::Genesis(_) => LedgerTransactionKind::Genesis,
293            Self::User(_) => LedgerTransactionKind::User,
294            Self::Validator(_) => LedgerTransactionKind::Validator,
295            Self::ProtocolUpdate(_) => LedgerTransactionKind::ProtocolUpdate,
296        }
297    }
298
299    pub fn get_ledger_hash(&self) -> LedgerTransactionHash {
300        LedgerTransactionHash::for_kind(self.get_kind(), &self.get_summary().hash)
301    }
302}
303
304impl HasSummary for PreparedLedgerTransactionInner {
305    fn get_summary(&self) -> &Summary {
306        match self {
307            Self::Genesis(t) => t.get_summary(),
308            Self::User(t) => t.get_summary(),
309            Self::Validator(t) => t.get_summary(),
310            Self::ProtocolUpdate(t) => t.get_summary(),
311        }
312    }
313
314    fn summary_mut(&mut self) -> &mut Summary {
315        match self {
316            Self::Genesis(t) => t.summary_mut(),
317            Self::User(t) => t.summary_mut(),
318            Self::Validator(t) => t.summary_mut(),
319            Self::ProtocolUpdate(t) => t.summary_mut(),
320        }
321    }
322}
323
324impl TransactionPreparableFromValue for PreparedLedgerTransactionInner {
325    fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
326        decoder.track_stack_depth_increase()?;
327        let (discriminator, length) = decoder.read_enum_header()?;
328        let prepared_inner = match discriminator {
329            GENESIS_LEDGER_TRANSACTION_DISCRIMINATOR => {
330                check_length(length, 1)?;
331                let (discriminator, length) = decoder.read_enum_header()?;
332                let genesis_transaction = match discriminator {
333                    GENESIS_TRANSACTION_FLASH_DISCRIMINATOR => {
334                        check_length(length, 0)?;
335                        PreparedGenesisTransaction::Flash(Summary {
336                            effective_length: 0,
337                            total_bytes_hashed: 0,
338                            hash: hash("Genesis Flash"),
339                        })
340                    }
341                    GENESIS_TRANSACTION_SYSTEM_TRANSACTION_DISCRIMINATOR => {
342                        check_length(length, 1)?;
343                        let prepared = PreparedSystemTransactionV1::prepare_from_value(decoder)?;
344                        PreparedGenesisTransaction::Transaction(prepared)
345                    }
346                    _ => return Err(unknown_discriminator(discriminator)),
347                };
348                PreparedLedgerTransactionInner::Genesis(genesis_transaction)
349            }
350            USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
351                check_length(length, 1)?;
352                let prepared = PreparedNotarizedTransactionV1::prepare_from_value(decoder)?;
353                PreparedLedgerTransactionInner::User(PreparedUserTransaction::V1(prepared))
354            }
355            ROUND_UPDATE_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
356                check_length(length, 1)?;
357                let prepared = PreparedRoundUpdateTransactionV1::prepare_from_value(decoder)?;
358                PreparedLedgerTransactionInner::Validator(prepared)
359            }
360            FLASH_V1_LEDGER_TRANSACTION_DISCRIMINATOR => {
361                check_length(length, 1)?;
362                let prepared = PreparedFlashTransactionV1::prepare_from_value(decoder)?;
363                PreparedLedgerTransactionInner::ProtocolUpdate(prepared)
364            }
365            USER_V2_LEDGER_TRANSACTION_DISCRIMINATOR => {
366                check_length(length, 1)?;
367                let prepared = PreparedNotarizedTransactionV2::prepare_from_value(decoder)?;
368                PreparedLedgerTransactionInner::User(PreparedUserTransaction::V2(prepared))
369            }
370            _ => return Err(unknown_discriminator(discriminator)),
371        };
372        decoder.track_stack_depth_decrease()?;
373
374        Ok(prepared_inner)
375    }
376}
377
378fn check_length(actual: usize, expected: usize) -> Result<(), PrepareError> {
379    if actual != expected {
380        return Err(PrepareError::DecodeError(DecodeError::UnexpectedSize {
381            expected,
382            actual,
383        }));
384    }
385    Ok(())
386}
387
388fn unknown_discriminator(discriminator: u8) -> PrepareError {
389    PrepareError::DecodeError(DecodeError::UnknownDiscriminator(discriminator))
390}
391
392pub enum PreparedGenesisTransaction {
393    Flash(Summary),
394    Transaction(PreparedSystemTransactionV1),
395}
396
397impl HasSummary for PreparedGenesisTransaction {
398    fn get_summary(&self) -> &Summary {
399        match self {
400            PreparedGenesisTransaction::Flash(summary) => summary,
401            PreparedGenesisTransaction::Transaction(t) => t.get_summary(),
402        }
403    }
404
405    fn summary_mut(&mut self) -> &mut Summary {
406        match self {
407            PreparedGenesisTransaction::Flash(summary) => summary,
408            PreparedGenesisTransaction::Transaction(t) => t.summary_mut(),
409        }
410    }
411}
412
413impl HasSystemTransactionHash for PreparedGenesisTransaction {
414    fn system_transaction_hash(&self) -> SystemTransactionHash {
415        match self {
416            PreparedGenesisTransaction::Flash(summary) => SystemTransactionHash(summary.hash),
417            PreparedGenesisTransaction::Transaction(transaction) => {
418                transaction.system_transaction_hash()
419            }
420        }
421    }
422}
423
424impl PreparedTransaction for PreparedLedgerTransaction {
425    type Raw = RawLedgerTransaction;
426
427    fn prepare_from_transaction_enum(
428        decoder: &mut TransactionDecoder,
429    ) -> Result<Self, PrepareError> {
430        decoder.track_stack_depth_increase()?;
431        decoder.read_header(
432            ExpectedTupleHeader::EnumWithValueKind {
433                discriminator: TransactionDiscriminator::Ledger as u8,
434            },
435            1,
436        )?;
437        let inner = PreparedLedgerTransactionInner::prepare_from_value(decoder)?;
438        decoder.track_stack_depth_decrease()?;
439
440        let summary = Summary {
441            effective_length: inner.get_summary().effective_length,
442            total_bytes_hashed: inner.get_summary().total_bytes_hashed,
443            hash: inner.get_ledger_hash().0,
444        };
445        Ok(Self { inner, summary })
446    }
447}
448
449impl IntoExecutable for PreparedLedgerTransaction {
450    type Error = LedgerTransactionValidationError;
451
452    fn into_executable(
453        self,
454        validator: &TransactionValidator,
455    ) -> Result<ExecutableTransaction, Self::Error> {
456        self.validate(validator, AcceptedLedgerTransactionKind::Any)?
457            .into_executable(validator)
458    }
459}
460
461pub struct ValidatedLedgerTransaction {
462    pub inner: ValidatedLedgerTransactionInner,
463    pub summary: Summary,
464}
465
466pub enum ValidatedLedgerTransactionInner {
467    Genesis(PreparedGenesisTransaction),
468    User(ValidatedUserTransaction),
469    Validator(PreparedRoundUpdateTransactionV1),
470    ProtocolUpdate(PreparedFlashTransactionV1),
471}
472
473impl ValidatedLedgerTransaction {
474    pub fn intent_hash_if_user(&self) -> Option<TransactionIntentHash> {
475        match &self.inner {
476            ValidatedLedgerTransactionInner::Genesis(_) => None,
477            ValidatedLedgerTransactionInner::User(t) => Some(t.transaction_intent_hash()),
478            ValidatedLedgerTransactionInner::Validator(_) => None,
479            ValidatedLedgerTransactionInner::ProtocolUpdate(_) => None,
480        }
481    }
482
483    pub fn create_ledger_executable(self) -> LedgerExecutable {
484        match self.inner {
485            ValidatedLedgerTransactionInner::Genesis(genesis) => match genesis {
486                PreparedGenesisTransaction::Flash(_) => LedgerExecutable::GenesisFlash,
487                PreparedGenesisTransaction::Transaction(t) => LedgerExecutable::Transaction {
488                    executable: t
489                        .create_executable(btreeset!(system_execution(SystemExecution::Protocol))),
490                },
491            },
492            ValidatedLedgerTransactionInner::User(t) => LedgerExecutable::Transaction {
493                executable: t.create_executable(),
494            },
495            ValidatedLedgerTransactionInner::Validator(t) => LedgerExecutable::Transaction {
496                executable: t.create_executable(),
497            },
498            ValidatedLedgerTransactionInner::ProtocolUpdate(t) => LedgerExecutable::Flash {
499                updates: t.state_updates,
500            },
501        }
502    }
503
504    /// Returns an error if the transaction is a flash
505    pub fn create_executable(
506        self,
507    ) -> Result<ExecutableTransaction, LedgerTransactionValidationError> {
508        match self.create_ledger_executable() {
509            LedgerExecutable::GenesisFlash | LedgerExecutable::Flash { .. } => {
510                Err(LedgerTransactionValidationError::FlashNotCurrentlyPermitted)
511            }
512            LedgerExecutable::Transaction { executable } => Ok(executable),
513        }
514    }
515
516    pub fn create_hashes(&self) -> LedgerTransactionHashes {
517        LedgerTransactionHashes {
518            ledger_transaction_hash: self.ledger_transaction_hash(),
519            kinded: match &self.inner {
520                ValidatedLedgerTransactionInner::Genesis(t) => KindedTransactionHashes::Genesis {
521                    system_transaction_hash: t.system_transaction_hash(),
522                },
523                ValidatedLedgerTransactionInner::User(t) => {
524                    KindedTransactionHashes::User(t.hashes())
525                }
526                ValidatedLedgerTransactionInner::Validator(t) => {
527                    KindedTransactionHashes::RoundUpdateV1 {
528                        round_update_hash: t.round_update_transaction_hash(),
529                    }
530                }
531                ValidatedLedgerTransactionInner::ProtocolUpdate(t) => {
532                    KindedTransactionHashes::FlashV1 {
533                        flash_transaction_hash: t.flash_transaction_hash(),
534                    }
535                }
536            },
537        }
538    }
539}
540
541#[derive(Debug, Clone, PartialEq, Eq)]
542pub struct IdentifiedLedgerExecutable {
543    pub executable: LedgerExecutable,
544    pub hashes: LedgerTransactionHashes,
545}
546
547#[derive(Debug, Clone, PartialEq, Eq)]
548pub enum LedgerExecutable {
549    /// Should be resolved as create_system_bootstrap_flash() but due to crate issues it can't be
550    GenesisFlash,
551    Flash {
552        /// Can be converted into a FlashReceipt with a before_store
553        /// and then to a TransactionReceipt.
554        updates: StateUpdates,
555    },
556    Transaction {
557        executable: ExecutableTransaction,
558    },
559}
560
561impl IntoExecutable for ValidatedLedgerTransaction {
562    type Error = LedgerTransactionValidationError;
563
564    fn into_executable(
565        self,
566        _validator: &TransactionValidator,
567    ) -> Result<ExecutableTransaction, Self::Error> {
568        self.create_executable()
569    }
570}
571
572define_versioned! {
573    // `LedgerTransactionHashes` is used in the node's `VersionedCommittedTransactionIdentifiers`,
574    // so we add this here tp catch possible backwards compatibility with the node integrations,
575    // and ensure we have versioned models here ready to go for the node integration.
576    #[derive(Debug, Clone, ScryptoSbor)]
577    pub VersionedLedgerTransactionHashes(LedgerTransactionHashesVersions) {
578        previous_versions: [
579            1 => LedgerTransactionHashesV1: { updates_to: 2 },
580        ],
581        latest_version: {
582            2 => LedgerTransactionHashes = LedgerTransactionHashesV2,
583        },
584    },
585    outer_attributes: [
586        #[derive(ScryptoSborAssertion)]
587        #[sbor_assert(backwards_compatible(
588            bottlenose = "FILE:ledger_transaction_hashes_bottlenose.bin",
589            cuttlefish = "FILE:ledger_transaction_hashes_cuttlefish.bin"
590        ))]
591    ]
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
595pub struct LedgerTransactionHashesV2 {
596    pub ledger_transaction_hash: LedgerTransactionHash,
597    pub kinded: KindedTransactionHashesV2,
598}
599
600#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
601pub struct LedgerTransactionHashesV1 {
602    pub ledger_transaction_hash: LedgerTransactionHash,
603    pub kinded: KindedTransactionHashesV1,
604}
605
606impl From<LedgerTransactionHashesV1> for LedgerTransactionHashesV2 {
607    fn from(value: LedgerTransactionHashesV1) -> Self {
608        let LedgerTransactionHashesV1 {
609            ledger_transaction_hash,
610            kinded,
611        } = value;
612        LedgerTransactionHashesV2 {
613            ledger_transaction_hash,
614            kinded: kinded.into(),
615        }
616    }
617}
618
619impl LedgerTransactionHashes {
620    pub fn as_user(&self) -> Option<&UserTransactionHashes> {
621        self.kinded.as_user()
622    }
623}
624
625pub type KindedTransactionHashes = KindedTransactionHashesV2;
626
627impl From<KindedTransactionHashesV1> for KindedTransactionHashesV2 {
628    fn from(value: KindedTransactionHashesV1) -> Self {
629        match value {
630            KindedTransactionHashesV1::Genesis {
631                system_transaction_hash,
632            } => KindedTransactionHashesV2::Genesis {
633                system_transaction_hash,
634            },
635            KindedTransactionHashesV1::User(user_transaction_hashes_v1) => {
636                KindedTransactionHashesV2::User(user_transaction_hashes_v1.into())
637            }
638            KindedTransactionHashesV1::RoundUpdateV1 { round_update_hash } => {
639                KindedTransactionHashesV2::RoundUpdateV1 { round_update_hash }
640            }
641            KindedTransactionHashesV1::FlashV1 {
642                flash_transaction_hash,
643            } => KindedTransactionHashesV2::FlashV1 {
644                flash_transaction_hash,
645            },
646        }
647    }
648}
649
650#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
651pub enum KindedTransactionHashesV1 {
652    Genesis {
653        system_transaction_hash: SystemTransactionHash,
654    },
655    User(#[sbor(flatten)] UserTransactionHashesV1),
656    RoundUpdateV1 {
657        round_update_hash: RoundUpdateTransactionHash,
658    },
659    FlashV1 {
660        flash_transaction_hash: FlashTransactionHash,
661    },
662}
663
664#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
665pub enum KindedTransactionHashesV2 {
666    Genesis {
667        system_transaction_hash: SystemTransactionHash,
668    },
669    User(#[sbor(flatten)] UserTransactionHashesV2),
670    RoundUpdateV1 {
671        round_update_hash: RoundUpdateTransactionHash,
672    },
673    FlashV1 {
674        flash_transaction_hash: FlashTransactionHash,
675    },
676}
677
678impl KindedTransactionHashes {
679    pub fn as_user(&self) -> Option<&UserTransactionHashes> {
680        match self {
681            KindedTransactionHashes::User(user) => Some(user),
682            _ => None,
683        }
684    }
685}
686
687impl HasLedgerTransactionHash for ValidatedLedgerTransaction {
688    fn ledger_transaction_hash(&self) -> LedgerTransactionHash {
689        LedgerTransactionHash::from_hash(self.summary.hash)
690    }
691}
692
693define_wrapped_hash!(LedgerTransactionHash);
694
695impl LedgerTransactionHash {
696    pub fn for_genesis(hash: &SystemTransactionHash) -> Self {
697        Self::for_kind(LedgerTransactionKind::Genesis, &hash.0)
698    }
699
700    pub fn for_user(hash: &NotarizedTransactionHash) -> Self {
701        Self::for_kind(LedgerTransactionKind::User, &hash.0)
702    }
703
704    pub fn for_round_update(hash: &RoundUpdateTransactionHash) -> Self {
705        Self::for_kind(LedgerTransactionKind::Validator, &hash.0)
706    }
707
708    fn for_kind(kind: LedgerTransactionKind, inner: &Hash) -> Self {
709        Self(
710            HashAccumulator::new()
711                .concat([
712                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
713                    TransactionDiscriminator::Ledger as u8,
714                    kind.discriminator_for_hash(),
715                ])
716                .concat(inner.as_slice())
717                .finalize(),
718        )
719    }
720}
721
722impl IsTransactionHashWithStaticHrp for LedgerTransactionHash {
723    fn static_hrp(hrp_set: &HrpSet) -> &str {
724        &hrp_set.ledger_transaction
725    }
726}
727
728pub trait HasLedgerTransactionHash {
729    fn ledger_transaction_hash(&self) -> LedgerTransactionHash;
730}
731
732impl HasLedgerTransactionHash for PreparedLedgerTransaction {
733    fn ledger_transaction_hash(&self) -> LedgerTransactionHash {
734        LedgerTransactionHash::from_hash(self.summary.hash)
735    }
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741
742    #[test]
743    pub fn v1_ledger_transaction_structure() {
744        let sig_1_private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
745        let sig_2_private_key = Ed25519PrivateKey::from_u64(2).unwrap();
746        let notary_private_key = Ed25519PrivateKey::from_u64(3).unwrap();
747
748        let notarized = TransactionBuilder::new()
749            .header(TransactionHeaderV1 {
750                network_id: 21,
751                start_epoch_inclusive: Epoch::of(0),
752                end_epoch_exclusive: Epoch::of(100),
753                nonce: 0,
754                notary_public_key: notary_private_key.public_key().into(),
755                notary_is_signatory: true,
756                tip_percentage: 0,
757            })
758            .manifest(ManifestBuilder::new().drop_all_proofs().build())
759            .sign(&sig_1_private_key)
760            .sign(&sig_2_private_key)
761            .notarize(&notary_private_key)
762            .build();
763
764        let prepared_notarized = notarized
765            .prepare(PreparationSettings::latest_ref())
766            .expect("Notarized can be prepared");
767
768        let ledger = LedgerTransaction::UserV1(Box::new(notarized));
769        let raw_ledger_transaction = ledger.to_raw().expect("Can be encoded");
770        LedgerTransaction::from_raw(&raw_ledger_transaction).expect("Can be decoded");
771        let prepared_ledger_transaction = raw_ledger_transaction
772            .prepare(PreparationSettings::latest_ref())
773            .expect("Can be prepared");
774
775        let expected_intent_hash = LedgerTransactionHash::from_hash(hash(
776            [
777                [
778                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
779                    TransactionDiscriminator::Ledger as u8,
780                    USER_V1_LEDGER_TRANSACTION_DISCRIMINATOR,
781                ]
782                .as_slice(),
783                prepared_notarized.notarized_transaction_hash().0.as_slice(),
784            ]
785            .concat(),
786        ));
787        assert_eq!(
788            prepared_ledger_transaction.ledger_transaction_hash(),
789            expected_intent_hash
790        );
791        assert_eq!(
792            LedgerTransactionHash::for_user(&prepared_notarized.notarized_transaction_hash()),
793            expected_intent_hash
794        );
795    }
796}