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