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
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    /// Returns an error if the transaction is a flash
508    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    /// Should be resolved as create_system_bootstrap_flash() but due to crate issues it can't be
554    GenesisFlash,
555    Flash {
556        /// Can be converted into a FlashReceipt with a before_store
557        /// and then to a TransactionReceipt.
558        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    // `LedgerTransactionHashes` is used in the node's `VersionedCommittedTransactionIdentifiers`,
578    // so we add this here tp catch possible backwards compatibility with the node integrations,
579    // and ensure we have versioned models here ready to go for the node integration.
580    #[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(&notary_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}