diem_types/transaction/
mod.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use std::{
9    convert::TryFrom,
10    fmt::{self, Display, Formatter},
11    ops::Deref,
12};
13
14use anyhow::{ensure, format_err, Error, Result};
15#[cfg(any(test, feature = "fuzzing"))]
16use proptest_derive::Arbitrary;
17use serde::{Deserialize, Serialize};
18
19use diem_crypto::{
20    hash::{CryptoHash, EventAccumulatorHasher},
21    traits::SigningKey,
22    HashValue, PrivateKey, VRFProof,
23};
24use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
25use pow_types::StakingEvent;
26
27use crate::{
28    account_address::AccountAddress,
29    block_info::PivotBlockDecision,
30    block_metadata::BlockMetadata,
31    chain_id::ChainId,
32    contract_event::ContractEvent,
33    ledger_info::LedgerInfo,
34    proof::{accumulator::InMemoryAccumulator, TransactionInfoWithProof},
35    term_state::{
36        DisputeEvent, ElectionEvent, NodeID, RegisterEvent, RetireEvent,
37        UpdateVotingPowerEvent,
38    },
39    transaction::authenticator::{
40        TransactionAuthenticator, TransactionAuthenticatorUnchecked,
41    },
42    validator_config::{
43        ConsensusPrivateKey, ConsensusPublicKey, ConsensusSignature,
44        ConsensusVRFProof, ConsensusVRFPublicKey, MultiConsensusSignature,
45    },
46    vm_status::{DiscardedVMStatus, KeptVMStatus, StatusCode, VMStatus},
47};
48
49pub mod authenticator;
50
51pub type Version = u64; // Height - also used for MVCC in StateDB
52
53// In StateDB, things readable by the genesis transaction are under this
54// version.
55pub const PRE_GENESIS_VERSION: Version = u64::max_value();
56
57/// RawTransaction is the portion of a transaction that a client signs.
58#[derive(
59    Clone,
60    Debug,
61    Hash,
62    Eq,
63    PartialEq,
64    Serialize,
65    Deserialize,
66    CryptoHasher,
67    BCSCryptoHash,
68)]
69pub struct RawTransaction {
70    /// Sender's address.
71    sender: AccountAddress,
72
73    /// The transaction payload, e.g., a script to execute.
74    payload: TransactionPayload,
75
76    /// Expiration timestamp for this transaction, represented
77    /// as seconds from the Unix Epoch. If the current blockchain timestamp
78    /// is greater than or equal to this time, then the transaction has
79    /// expired and will be discarded. This can be set to a large value far
80    /// in the future to indicate that a transaction does not expire.
81    expiration_timestamp_secs: u64,
82
83    /// Chain ID of the Diem network this transaction is intended for.
84    chain_id: ChainId,
85}
86
87impl RawTransaction {
88    /// Create a new `RawTransaction` with a payload.
89    ///
90    /// It can be either to publish a module, to execute a script, or to issue a
91    /// writeset transaction.
92    pub fn new(
93        sender: AccountAddress, payload: TransactionPayload,
94        expiration_timestamp_secs: u64, chain_id: ChainId,
95    ) -> Self {
96        RawTransaction {
97            sender,
98            payload,
99            expiration_timestamp_secs,
100            chain_id,
101        }
102    }
103
104    pub fn new_pivot_decision(
105        sender: AccountAddress, pivot_decision: PivotBlockDecision,
106        chain_id: ChainId,
107    ) -> Self {
108        RawTransaction {
109            sender,
110            payload: TransactionPayload::PivotDecision(pivot_decision),
111            // Write-set transactions are special and important and shouldn't
112            // expire.
113            expiration_timestamp_secs: u64::max_value(),
114            chain_id,
115        }
116    }
117
118    pub fn new_election(
119        sender: AccountAddress, election_payload: ElectionPayload,
120        chain_id: ChainId,
121    ) -> Self {
122        RawTransaction {
123            sender,
124            payload: TransactionPayload::Election(election_payload),
125            // Write-set transactions are special and important and shouldn't
126            // expire.
127            expiration_timestamp_secs: u64::max_value(),
128            chain_id,
129        }
130    }
131
132    pub fn new_dispute(
133        sender: AccountAddress, dispute_payload: DisputePayload,
134    ) -> Self {
135        RawTransaction {
136            sender,
137            payload: TransactionPayload::Dispute(dispute_payload),
138            // Write-set transactions are special and important and shouldn't
139            // expire.
140            expiration_timestamp_secs: u64::max_value(),
141            chain_id: Default::default(),
142        }
143    }
144
145    pub fn new_retire(
146        sender: AccountAddress, retire_payload: RetirePayload,
147    ) -> Self {
148        RawTransaction {
149            sender,
150            payload: TransactionPayload::Retire(retire_payload),
151            // Write-set transactions are special and important and shouldn't
152            // expire.
153            expiration_timestamp_secs: u64::max_value(),
154            chain_id: Default::default(),
155        }
156    }
157
158    pub fn from_staking_event(
159        staking_event: &StakingEvent, sender: AccountAddress,
160    ) -> Result<Self> {
161        let payload = match staking_event {
162            StakingEvent::Register(
163                addr_h256,
164                bls_pub_key_bytes,
165                vrf_pub_key_bytes,
166            ) => {
167                let addr = AccountAddress::from_bytes(addr_h256)?;
168                let public_key =
169                    ConsensusPublicKey::try_from(bls_pub_key_bytes.as_slice())?;
170                let vrf_public_key = ConsensusVRFPublicKey::try_from(
171                    vrf_pub_key_bytes.as_slice(),
172                )?;
173                let node_id =
174                    NodeID::new(public_key.clone(), vrf_public_key.clone());
175                ensure!(
176                    node_id.addr == addr,
177                    "register event has unmatching address and keys"
178                );
179                TransactionPayload::Register(RegisterPayload {
180                    public_key,
181                    vrf_public_key,
182                })
183            }
184            StakingEvent::IncreaseStake(addr_h256, updated_voting_power) => {
185                let addr = AccountAddress::from_bytes(addr_h256)?;
186                TransactionPayload::UpdateVotingPower(
187                    UpdateVotingPowerPayload {
188                        node_address: addr,
189                        voting_power: *updated_voting_power,
190                    },
191                )
192            }
193            StakingEvent::Retire(identifier, votes) => {
194                TransactionPayload::Retire(RetirePayload {
195                    node_id: AccountAddress::new(identifier.0),
196                    votes: *votes,
197                })
198            }
199        };
200        Ok(RawTransaction {
201            sender,
202            payload,
203            // Write-set transactions are special and important and shouldn't
204            // expire.
205            expiration_timestamp_secs: u64::max_value(),
206            chain_id: Default::default(),
207        })
208    }
209
210    /// Signs the given `RawTransaction`. Note that this consumes the
211    /// `RawTransaction` and turns it into a `SignatureCheckedTransaction`.
212    ///
213    /// For a transaction that has just been signed, its signature is expected
214    /// to be valid.
215    pub fn sign(
216        self, private_key: &ConsensusPrivateKey,
217    ) -> Result<SignatureCheckedTransaction> {
218        let signature = match self.payload {
219            TransactionPayload::PivotDecision(ref pivot_decision) => {
220                private_key.sign(pivot_decision)
221            }
222            _ => private_key.sign(&self),
223        };
224        let public_key = private_key.public_key();
225        Ok(SignatureCheckedTransaction(SignedTransaction::new(
226            self, public_key, signature,
227        )))
228    }
229
230    pub fn into_payload(self) -> TransactionPayload { self.payload }
231
232    /// Return the sender of this transaction.
233    pub fn sender(&self) -> AccountAddress { self.sender }
234}
235
236/// Different kinds of transactions.
237///
238/// **BCS serialization note:** Variant indices must remain stable for
239/// database compatibility. Indices 1-3 are legacy Diem Move variants that
240/// were never used in Conflux PoS but must be preserved as placeholders.
241#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
242pub enum TransactionPayload {
243    /// Legacy Diem variant (index 0). Never used in Conflux PoS.
244    #[doc(hidden)]
245    _LegacyWriteSet,
246    /// Legacy Diem variant (index 1). Never used in Conflux PoS.
247    #[doc(hidden)]
248    _LegacyScript,
249    /// Legacy Diem variant (index 2). Never used in Conflux PoS.
250    #[doc(hidden)]
251    _LegacyModule,
252    /// Legacy Diem variant (index 3). Never used in Conflux PoS.
253    #[doc(hidden)]
254    _LegacyScriptFunction,
255
256    /// A transaction that add a node to committee candidates.
257    Election(ElectionPayload),
258
259    /// A transaction that sets a node to `Retire` status so the node will not
260    /// be elected.
261    Retire(RetirePayload),
262
263    Register(RegisterPayload),
264
265    UpdateVotingPower(UpdateVotingPowerPayload),
266
267    PivotDecision(PivotBlockDecision),
268
269    Dispute(DisputePayload),
270}
271
272#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct ElectionPayload {
275    pub public_key: ConsensusPublicKey,
276    pub vrf_public_key: ConsensusVRFPublicKey,
277    pub target_term: u64,
278    pub vrf_proof: ConsensusVRFProof,
279}
280
281impl ElectionPayload {
282    pub fn to_event(&self) -> ContractEvent {
283        let event = ElectionEvent::new(
284            self.public_key.clone(),
285            self.vrf_public_key.clone(),
286            self.vrf_proof.to_hash().unwrap(),
287            self.target_term,
288        );
289        ContractEvent::new(
290            ElectionEvent::event_key(),
291            bcs::to_bytes(&event).unwrap(),
292        )
293    }
294}
295
296#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
297#[serde(rename_all = "camelCase")]
298pub struct RetirePayload {
299    pub node_id: AccountAddress,
300    pub votes: u64,
301}
302
303impl RetirePayload {
304    pub fn to_event(&self) -> ContractEvent {
305        let event = RetireEvent::new(self.node_id, self.votes);
306        ContractEvent::new(
307            RetireEvent::event_key(),
308            bcs::to_bytes(&event).unwrap(),
309        )
310    }
311}
312
313#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
314#[serde(rename_all = "camelCase")]
315pub struct RegisterPayload {
316    pub public_key: ConsensusPublicKey,
317    pub vrf_public_key: ConsensusVRFPublicKey,
318}
319
320impl RegisterPayload {
321    pub fn to_event(&self) -> ContractEvent {
322        let event = RegisterEvent::new(
323            self.public_key.clone(),
324            self.vrf_public_key.clone(),
325        );
326        ContractEvent::new(
327            RegisterEvent::event_key(),
328            bcs::to_bytes(&event).unwrap(),
329        )
330    }
331}
332
333#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
334#[serde(rename_all = "camelCase")]
335pub struct UpdateVotingPowerPayload {
336    pub node_address: AccountAddress,
337    pub voting_power: u64,
338}
339
340impl UpdateVotingPowerPayload {
341    pub fn to_event(&self) -> ContractEvent {
342        let event = UpdateVotingPowerEvent::new(
343            self.node_address.clone(),
344            self.voting_power,
345        );
346        ContractEvent::new(
347            UpdateVotingPowerEvent::event_key(),
348            bcs::to_bytes(&event).unwrap(),
349        )
350    }
351}
352
353#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
354#[serde(rename_all = "camelCase")]
355pub struct DisputePayload {
356    pub address: AccountAddress,
357    pub bls_pub_key: ConsensusPublicKey,
358    pub vrf_pub_key: ConsensusVRFPublicKey,
359    pub conflicting_votes: ConflictSignature,
360}
361
362#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
363pub enum ConflictSignature {
364    // Use raw bytes instead of `Proposal` or `Vote` to avoid dependency loop.
365    Proposal((Vec<u8>, Vec<u8>)),
366    Vote((Vec<u8>, Vec<u8>)),
367}
368
369impl DisputePayload {
370    pub fn to_event(&self) -> ContractEvent {
371        let event = DisputeEvent {
372            node_id: self.address,
373        };
374        ContractEvent::new(
375            DisputeEvent::event_key(),
376            bcs::to_bytes(&event).unwrap(),
377        )
378    }
379}
380
381/// A transaction that has been signed.
382///
383/// A `SignedTransaction` is a single transaction that can be atomically
384/// executed. Clients submit these to validator nodes, and the validator and
385/// executor submits these to the VM.
386///
387/// **IMPORTANT:** The signature of a `SignedTransaction` is not guaranteed to
388/// be verified. For a transaction whose signature is statically guaranteed to
389/// be verified, see [`SignatureCheckedTransaction`].
390#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
391pub struct SignedTransaction {
392    /// The raw transaction
393    raw_txn: RawTransaction,
394
395    /// Public key and signature to authenticate
396    authenticator: TransactionAuthenticator,
397}
398
399#[derive(Deserialize)]
400pub struct SignedTransactionUnchecked {
401    pub raw_txn: RawTransaction,
402    pub authenticator: TransactionAuthenticatorUnchecked,
403}
404
405impl From<SignedTransactionUnchecked> for SignedTransaction {
406    fn from(t: SignedTransactionUnchecked) -> Self {
407        Self {
408            raw_txn: t.raw_txn,
409            authenticator: t.authenticator.into(),
410        }
411    }
412}
413
414/// A transaction for which the signature has been verified. Created by
415/// [`SignedTransaction::check_signature`] and [`RawTransaction::sign`].
416#[derive(Clone, Debug, Eq, PartialEq, Hash)]
417pub struct SignatureCheckedTransaction(SignedTransaction);
418
419impl SignatureCheckedTransaction {
420    /// Returns the `SignedTransaction` within.
421    pub fn into_inner(self) -> SignedTransaction { self.0 }
422
423    /// Returns the `RawTransaction` within.
424    pub fn into_raw_transaction(self) -> RawTransaction {
425        self.0.into_raw_transaction()
426    }
427}
428
429impl Deref for SignatureCheckedTransaction {
430    type Target = SignedTransaction;
431
432    fn deref(&self) -> &Self::Target { &self.0 }
433}
434
435impl fmt::Debug for SignedTransaction {
436    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437        write!(
438            f,
439            "SignedTransaction {{ \n \
440             {{ raw_txn: {:#?}, \n \
441             authenticator: {:#?}, \n \
442             }} \n \
443             }}",
444            self.raw_txn, self.authenticator
445        )
446    }
447}
448
449impl SignedTransaction {
450    pub fn new(
451        raw_txn: RawTransaction, public_key: ConsensusPublicKey,
452        signature: ConsensusSignature,
453    ) -> SignedTransaction {
454        let authenticator =
455            TransactionAuthenticator::bls(public_key, signature);
456        SignedTransaction {
457            raw_txn,
458            authenticator,
459        }
460    }
461
462    pub fn new_multisig(
463        raw_txn: RawTransaction, signatures: Vec<(ConsensusSignature, usize)>,
464    ) -> SignedTransaction {
465        let signature = MultiConsensusSignature::new(signatures).unwrap();
466        let authenticator = TransactionAuthenticator::multi_bls(signature);
467        SignedTransaction {
468            raw_txn,
469            authenticator,
470        }
471    }
472
473    pub fn authenticator(&self) -> TransactionAuthenticator {
474        self.authenticator.clone()
475    }
476
477    pub fn raw_txn(&self) -> RawTransaction { self.raw_txn.clone() }
478
479    pub fn hash(&self) -> HashValue { self.raw_txn.hash() }
480
481    pub fn sender(&self) -> AccountAddress { self.raw_txn.sender }
482
483    pub fn into_raw_transaction(self) -> RawTransaction { self.raw_txn }
484
485    pub fn chain_id(&self) -> ChainId { self.raw_txn.chain_id }
486
487    pub fn payload(&self) -> &TransactionPayload { &self.raw_txn.payload }
488
489    pub fn expiration_timestamp_secs(&self) -> u64 {
490        self.raw_txn.expiration_timestamp_secs
491    }
492
493    pub fn raw_txn_bytes_len(&self) -> usize {
494        bcs::to_bytes(&self.raw_txn)
495            .expect("Unable to serialize RawTransaction")
496            .len()
497    }
498
499    /// Checks that the signature of given transaction. Returns
500    /// `Ok(SignatureCheckedTransaction)` if the signature is valid.
501    pub fn check_signature(self) -> Result<SignatureCheckedTransaction> {
502        match self.payload() {
503            TransactionPayload::PivotDecision(pivot_decision) => {
504                self.authenticator.verify(pivot_decision)?
505            }
506            _ => self.authenticator.verify(&self.raw_txn)?,
507        }
508        Ok(SignatureCheckedTransaction(self))
509    }
510}
511
512#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
513#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
514pub struct TransactionWithProof {
515    pub version: Version,
516    pub transaction: Transaction,
517    pub events: Option<Vec<ContractEvent>>,
518    pub proof: TransactionInfoWithProof,
519}
520
521impl TransactionWithProof {
522    pub fn new(
523        version: Version, transaction: Transaction,
524        events: Option<Vec<ContractEvent>>, proof: TransactionInfoWithProof,
525    ) -> Self {
526        Self {
527            version,
528            transaction,
529            events,
530            proof,
531        }
532    }
533
534    /// Verifies the transaction with the proof, both carried by `self`.
535    ///
536    /// A few things are ensured if no error is raised:
537    ///   1. This transaction exists in the ledger represented by `ledger_info`.
538    ///   2. This transaction is a `UserTransaction`.
539    ///   3. And this user transaction has the same `version`, `sender`, and
540    /// `sequence_number` as      indicated by the parameter list. If any of
541    /// these parameter is unknown to the call site      that is supposed to
542    /// be informed via this struct, get it from the struct itself, such
543    ///      as version and sender.
544    pub fn verify_user_txn(
545        &self, ledger_info: &LedgerInfo, version: Version,
546        sender: AccountAddress,
547    ) -> Result<()> {
548        let signed_transaction = self.transaction.as_signed_user_txn()?;
549
550        ensure!(
551            self.version == version,
552            "Version ({}) is not expected ({}).",
553            self.version,
554            version,
555        );
556        ensure!(
557            signed_transaction.sender() == sender,
558            "Sender ({}) not expected ({}).",
559            signed_transaction.sender(),
560            sender,
561        );
562        let txn_hash = self.transaction.hash();
563        ensure!(
564            txn_hash == self.proof.transaction_info().transaction_hash,
565            "Transaction hash ({}) not expected ({}).",
566            txn_hash,
567            self.proof.transaction_info().transaction_hash,
568        );
569
570        if let Some(events) = &self.events {
571            let event_hashes: Vec<_> =
572                events.iter().map(ContractEvent::hash).collect();
573            let event_root_hash =
574                InMemoryAccumulator::<EventAccumulatorHasher>::from_leaves(
575                    &event_hashes[..],
576                )
577                .root_hash();
578            ensure!(
579                event_root_hash
580                    == self.proof.transaction_info().event_root_hash,
581                "Event root hash ({}) not expected ({}).",
582                event_root_hash,
583                self.proof.transaction_info().event_root_hash,
584            );
585        }
586
587        self.proof.verify(ledger_info, version)
588    }
589}
590
591/// The status of executing a transaction. The VM decides whether or not we
592/// should `Keep` the transaction output or `Discard` it based upon the
593/// execution of the transaction. We wrap these decisions around a `VMStatus`
594/// that provides more detail on the final execution state of the VM.
595#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
596pub enum TransactionStatus {
597    /// Discard the transaction output
598    Discard(DiscardedVMStatus),
599
600    /// Keep the transaction output
601    Keep(KeptVMStatus),
602
603    /// Retry the transaction, e.g., after a reconfiguration
604    Retry,
605}
606
607impl TransactionStatus {
608    pub fn status(&self) -> Result<KeptVMStatus, StatusCode> {
609        match self {
610            TransactionStatus::Keep(status) => Ok(status.clone()),
611            TransactionStatus::Discard(code) => Err(*code),
612            TransactionStatus::Retry => {
613                Err(StatusCode::UNKNOWN_VALIDATION_STATUS)
614            }
615        }
616    }
617
618    pub fn is_discarded(&self) -> bool {
619        match self {
620            TransactionStatus::Discard(_) => true,
621            TransactionStatus::Keep(_) => false,
622            TransactionStatus::Retry => true,
623        }
624    }
625}
626
627impl From<VMStatus> for TransactionStatus {
628    fn from(vm_status: VMStatus) -> Self {
629        match vm_status.keep_or_discard() {
630            Ok(recorded) => TransactionStatus::Keep(recorded),
631            Err(code) => TransactionStatus::Discard(code),
632        }
633    }
634}
635
636/// The output of executing a transaction.
637#[derive(Clone, Debug, Eq, PartialEq)]
638pub struct TransactionOutput {
639    /// The list of events emitted during this transaction.
640    events: Vec<ContractEvent>,
641
642    /// The amount of gas used during execution.
643    gas_used: u64,
644
645    /// The execution status.
646    status: TransactionStatus,
647}
648
649impl TransactionOutput {
650    pub fn new(
651        events: Vec<ContractEvent>, gas_used: u64, status: TransactionStatus,
652    ) -> Self {
653        TransactionOutput {
654            events,
655            gas_used,
656            status,
657        }
658    }
659
660    pub fn events(&self) -> &[ContractEvent] { &self.events }
661
662    pub fn gas_used(&self) -> u64 { self.gas_used }
663
664    pub fn status(&self) -> &TransactionStatus { &self.status }
665}
666
667/// `TransactionInfo` is the object we store in the transaction accumulator. It
668/// consists of the transaction as well as the execution result of this
669/// transaction.
670#[derive(
671    Clone,
672    CryptoHasher,
673    BCSCryptoHash,
674    Debug,
675    Eq,
676    PartialEq,
677    Serialize,
678    Deserialize,
679)]
680#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
681pub struct TransactionInfo {
682    /// The hash of this transaction.
683    transaction_hash: HashValue,
684
685    /// The root hash of Sparse Merkle Tree describing the world state at the
686    /// end of this transaction.
687    state_root_hash: HashValue,
688
689    /// The root hash of Merkle Accumulator storing all events emitted during
690    /// this transaction.
691    event_root_hash: HashValue,
692
693    /// The amount of gas used.
694    gas_used: u64,
695
696    /// The vm status. If it is not `Executed`, this will provide the general
697    /// error class. Execution failures and Move abort's recieve more
698    /// detailed information. But other errors are generally categorized
699    /// with no status code or other information
700    status: KeptVMStatus,
701}
702
703impl TransactionInfo {
704    /// Constructs a new `TransactionInfo` object using transaction hash, state
705    /// root hash and event root hash.
706    pub fn new(
707        transaction_hash: HashValue, state_root_hash: HashValue,
708        event_root_hash: HashValue, gas_used: u64, status: KeptVMStatus,
709    ) -> TransactionInfo {
710        TransactionInfo {
711            transaction_hash,
712            state_root_hash,
713            event_root_hash,
714            gas_used,
715            status,
716        }
717    }
718
719    /// Returns the hash of this transaction.
720    pub fn transaction_hash(&self) -> HashValue { self.transaction_hash }
721
722    /// Returns root hash of Sparse Merkle Tree describing the world state at
723    /// the end of this transaction.
724    pub fn state_root_hash(&self) -> HashValue { self.state_root_hash }
725
726    /// Returns the root hash of Merkle Accumulator storing all events emitted
727    /// during this transaction.
728    pub fn event_root_hash(&self) -> HashValue { self.event_root_hash }
729
730    /// Returns the amount of gas used by this transaction.
731    pub fn gas_used(&self) -> u64 { self.gas_used }
732
733    pub fn status(&self) -> &KeptVMStatus { &self.status }
734}
735
736impl Display for TransactionInfo {
737    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
738        write!(
739            f,
740            "TransactionInfo: [txn_hash: {}, state_root_hash: {}, event_root_hash: {}, gas_used: {}, recorded_status: {:?}]",
741            self.transaction_hash(), self.state_root_hash(), self.event_root_hash(), self.gas_used(), self.status(),
742        )
743    }
744}
745
746#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
747pub struct TransactionToCommit {
748    transaction: Transaction,
749    events: Vec<ContractEvent>,
750    gas_used: u64,
751    status: KeptVMStatus,
752}
753
754impl TransactionToCommit {
755    pub fn new(
756        transaction: Transaction, events: Vec<ContractEvent>, gas_used: u64,
757        status: KeptVMStatus,
758    ) -> Self {
759        TransactionToCommit {
760            transaction,
761            events,
762            gas_used,
763            status,
764        }
765    }
766
767    pub fn transaction(&self) -> &Transaction { &self.transaction }
768
769    pub fn events(&self) -> &[ContractEvent] { &self.events }
770
771    pub fn gas_used(&self) -> u64 { self.gas_used }
772
773    pub fn status(&self) -> &KeptVMStatus { &self.status }
774}
775
776/// `Transaction` will be the transaction type used internally in the diem node
777/// to represent the transaction to be processed and persisted.
778///
779/// We suppress the clippy warning here as we would expect most of the
780/// transaction to be user transaction.
781#[allow(clippy::large_enum_variant)]
782#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
783#[derive(
784    Clone,
785    Debug,
786    Eq,
787    PartialEq,
788    Serialize,
789    Deserialize,
790    CryptoHasher,
791    BCSCryptoHash,
792)]
793pub enum Transaction {
794    /// Transaction submitted by the user. e.g: P2P payment transaction,
795    /// publishing module transaction, etc.
796    /// TODO: We need to rename SignedTransaction to SignedUserTransaction, as
797    /// well as all the other       transaction types we had in our
798    /// codebase.
799    UserTransaction(SignedTransaction),
800
801    /// Genesis transaction carrying the epoch-change event for the
802    /// initial validator set.
803    GenesisTransaction(Vec<ContractEvent>),
804
805    /// Transaction to update the block metadata resource at the beginning
806    /// of a block.
807    BlockMetadata(BlockMetadata),
808}
809
810#[derive(Deserialize)]
811pub enum TransactionUnchecked {
812    UserTransaction(SignedTransactionUnchecked),
813    GenesisTransaction(Vec<ContractEvent>),
814    BlockMetadata(BlockMetadata),
815}
816
817impl From<TransactionUnchecked> for Transaction {
818    fn from(t: TransactionUnchecked) -> Self {
819        match t {
820            TransactionUnchecked::UserTransaction(t) => {
821                Self::UserTransaction(t.into())
822            }
823            TransactionUnchecked::GenesisTransaction(t) => {
824                Self::GenesisTransaction(t)
825            }
826            TransactionUnchecked::BlockMetadata(t) => Self::BlockMetadata(t),
827        }
828    }
829}
830
831impl Transaction {
832    pub fn as_signed_user_txn(&self) -> Result<&SignedTransaction> {
833        match self {
834            Transaction::UserTransaction(txn) => Ok(txn),
835            _ => Err(format_err!("Not a user transaction.")),
836        }
837    }
838}
839
840impl TryFrom<Transaction> for SignedTransaction {
841    type Error = Error;
842
843    fn try_from(txn: Transaction) -> Result<Self> {
844        match txn {
845            Transaction::UserTransaction(txn) => Ok(txn),
846            _ => Err(format_err!("Not a user transaction.")),
847        }
848    }
849}