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, DisputeEventV2, ElectionEvent, NodeID, RegisterEvent,
37        RetireEvent, 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    pub fn to_event_v2(&self, offense_epoch: u64) -> ContractEvent {
377        let event = DisputeEventV2 {
378            node_id: self.address,
379            offense_epoch,
380        };
381        ContractEvent::new(
382            DisputeEventV2::event_key(),
383            bcs::to_bytes(&event).unwrap(),
384        )
385    }
386}
387
388/// A transaction that has been signed.
389///
390/// A `SignedTransaction` is a single transaction that can be atomically
391/// executed. Clients submit these to validator nodes, and the validator and
392/// executor submits these to the VM.
393///
394/// **IMPORTANT:** The signature of a `SignedTransaction` is not guaranteed to
395/// be verified. For a transaction whose signature is statically guaranteed to
396/// be verified, see [`SignatureCheckedTransaction`].
397#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
398pub struct SignedTransaction {
399    /// The raw transaction
400    raw_txn: RawTransaction,
401
402    /// Public key and signature to authenticate
403    authenticator: TransactionAuthenticator,
404}
405
406#[derive(Deserialize)]
407pub struct SignedTransactionUnchecked {
408    pub raw_txn: RawTransaction,
409    pub authenticator: TransactionAuthenticatorUnchecked,
410}
411
412impl From<SignedTransactionUnchecked> for SignedTransaction {
413    fn from(t: SignedTransactionUnchecked) -> Self {
414        Self {
415            raw_txn: t.raw_txn,
416            authenticator: t.authenticator.into(),
417        }
418    }
419}
420
421/// A transaction for which the signature has been verified. Created by
422/// [`SignedTransaction::check_signature`] and [`RawTransaction::sign`].
423#[derive(Clone, Debug, Eq, PartialEq, Hash)]
424pub struct SignatureCheckedTransaction(SignedTransaction);
425
426impl SignatureCheckedTransaction {
427    /// Returns the `SignedTransaction` within.
428    pub fn into_inner(self) -> SignedTransaction { self.0 }
429
430    /// Returns the `RawTransaction` within.
431    pub fn into_raw_transaction(self) -> RawTransaction {
432        self.0.into_raw_transaction()
433    }
434}
435
436impl Deref for SignatureCheckedTransaction {
437    type Target = SignedTransaction;
438
439    fn deref(&self) -> &Self::Target { &self.0 }
440}
441
442impl fmt::Debug for SignedTransaction {
443    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444        write!(
445            f,
446            "SignedTransaction {{ \n \
447             {{ raw_txn: {:#?}, \n \
448             authenticator: {:#?}, \n \
449             }} \n \
450             }}",
451            self.raw_txn, self.authenticator
452        )
453    }
454}
455
456impl SignedTransaction {
457    pub fn new(
458        raw_txn: RawTransaction, public_key: ConsensusPublicKey,
459        signature: ConsensusSignature,
460    ) -> SignedTransaction {
461        let authenticator =
462            TransactionAuthenticator::bls(public_key, signature);
463        SignedTransaction {
464            raw_txn,
465            authenticator,
466        }
467    }
468
469    pub fn new_multisig(
470        raw_txn: RawTransaction, signatures: Vec<(ConsensusSignature, usize)>,
471    ) -> Result<SignedTransaction> {
472        // Return errors instead of panicking so a malformed aggregation
473        // can't crash proposal construction.
474        let signature = MultiConsensusSignature::new(signatures)?;
475        let authenticator = TransactionAuthenticator::multi_bls(signature);
476        Ok(SignedTransaction {
477            raw_txn,
478            authenticator,
479        })
480    }
481
482    pub fn authenticator(&self) -> TransactionAuthenticator {
483        self.authenticator.clone()
484    }
485
486    pub fn raw_txn(&self) -> RawTransaction { self.raw_txn.clone() }
487
488    pub fn hash(&self) -> HashValue { self.raw_txn.hash() }
489
490    pub fn sender(&self) -> AccountAddress { self.raw_txn.sender }
491
492    pub fn into_raw_transaction(self) -> RawTransaction { self.raw_txn }
493
494    pub fn chain_id(&self) -> ChainId { self.raw_txn.chain_id }
495
496    pub fn payload(&self) -> &TransactionPayload { &self.raw_txn.payload }
497
498    pub fn expiration_timestamp_secs(&self) -> u64 {
499        self.raw_txn.expiration_timestamp_secs
500    }
501
502    pub fn raw_txn_bytes_len(&self) -> usize {
503        bcs::to_bytes(&self.raw_txn)
504            .expect("Unable to serialize RawTransaction")
505            .len()
506    }
507
508    /// Verifies the authenticator's signature against the appropriate
509    /// signed message without consuming `self`. Returns `Ok(())` on a
510    /// valid signature.
511    pub fn verify_signature(&self) -> Result<()> {
512        match self.payload() {
513            TransactionPayload::PivotDecision(pivot_decision) => {
514                self.authenticator.verify(pivot_decision)
515            }
516            _ => self.authenticator.verify(&self.raw_txn),
517        }
518    }
519
520    /// Same as `verify_signature`, but consumes `self` and returns a
521    /// `SignatureCheckedTransaction` newtype that proves the check ran.
522    pub fn check_signature(self) -> Result<SignatureCheckedTransaction> {
523        self.verify_signature()?;
524        Ok(SignatureCheckedTransaction(self))
525    }
526}
527
528#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
529#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
530pub struct TransactionWithProof {
531    pub version: Version,
532    pub transaction: Transaction,
533    pub events: Option<Vec<ContractEvent>>,
534    pub proof: TransactionInfoWithProof,
535}
536
537impl TransactionWithProof {
538    pub fn new(
539        version: Version, transaction: Transaction,
540        events: Option<Vec<ContractEvent>>, proof: TransactionInfoWithProof,
541    ) -> Self {
542        Self {
543            version,
544            transaction,
545            events,
546            proof,
547        }
548    }
549
550    /// Verifies the transaction with the proof, both carried by `self`.
551    ///
552    /// A few things are ensured if no error is raised:
553    ///   1. This transaction exists in the ledger represented by `ledger_info`.
554    ///   2. This transaction is a `UserTransaction`.
555    ///   3. And this user transaction has the same `version`, `sender`, and
556    /// `sequence_number` as      indicated by the parameter list. If any of
557    /// these parameter is unknown to the call site      that is supposed to
558    /// be informed via this struct, get it from the struct itself, such
559    ///      as version and sender.
560    pub fn verify_user_txn(
561        &self, ledger_info: &LedgerInfo, version: Version,
562        sender: AccountAddress,
563    ) -> Result<()> {
564        let signed_transaction = self.transaction.as_signed_user_txn()?;
565
566        ensure!(
567            self.version == version,
568            "Version ({}) is not expected ({}).",
569            self.version,
570            version,
571        );
572        ensure!(
573            signed_transaction.sender() == sender,
574            "Sender ({}) not expected ({}).",
575            signed_transaction.sender(),
576            sender,
577        );
578        let txn_hash = self.transaction.hash();
579        ensure!(
580            txn_hash == self.proof.transaction_info().transaction_hash,
581            "Transaction hash ({}) not expected ({}).",
582            txn_hash,
583            self.proof.transaction_info().transaction_hash,
584        );
585
586        if let Some(events) = &self.events {
587            let event_hashes: Vec<_> =
588                events.iter().map(ContractEvent::hash).collect();
589            let event_root_hash =
590                InMemoryAccumulator::<EventAccumulatorHasher>::from_leaves(
591                    &event_hashes[..],
592                )
593                .root_hash();
594            ensure!(
595                event_root_hash
596                    == self.proof.transaction_info().event_root_hash,
597                "Event root hash ({}) not expected ({}).",
598                event_root_hash,
599                self.proof.transaction_info().event_root_hash,
600            );
601        }
602
603        self.proof.verify(ledger_info, version)
604    }
605}
606
607/// The status of executing a transaction. The VM decides whether or not we
608/// should `Keep` the transaction output or `Discard` it based upon the
609/// execution of the transaction. We wrap these decisions around a `VMStatus`
610/// that provides more detail on the final execution state of the VM.
611#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
612pub enum TransactionStatus {
613    /// Discard the transaction output
614    Discard(DiscardedVMStatus),
615
616    /// Keep the transaction output
617    Keep(KeptVMStatus),
618
619    /// Retry the transaction, e.g., after a reconfiguration
620    Retry,
621}
622
623impl TransactionStatus {
624    pub fn status(&self) -> Result<KeptVMStatus, StatusCode> {
625        match self {
626            TransactionStatus::Keep(status) => Ok(status.clone()),
627            TransactionStatus::Discard(code) => Err(*code),
628            TransactionStatus::Retry => {
629                Err(StatusCode::UNKNOWN_VALIDATION_STATUS)
630            }
631        }
632    }
633
634    pub fn is_discarded(&self) -> bool {
635        match self {
636            TransactionStatus::Discard(_) => true,
637            TransactionStatus::Keep(_) => false,
638            TransactionStatus::Retry => true,
639        }
640    }
641}
642
643impl From<VMStatus> for TransactionStatus {
644    fn from(vm_status: VMStatus) -> Self {
645        match vm_status.keep_or_discard() {
646            Ok(recorded) => TransactionStatus::Keep(recorded),
647            Err(code) => TransactionStatus::Discard(code),
648        }
649    }
650}
651
652/// The output of executing a transaction.
653#[derive(Clone, Debug, Eq, PartialEq)]
654pub struct TransactionOutput {
655    /// The list of events emitted during this transaction.
656    events: Vec<ContractEvent>,
657
658    /// The amount of gas used during execution.
659    gas_used: u64,
660
661    /// The execution status.
662    status: TransactionStatus,
663}
664
665impl TransactionOutput {
666    pub fn new(
667        events: Vec<ContractEvent>, gas_used: u64, status: TransactionStatus,
668    ) -> Self {
669        TransactionOutput {
670            events,
671            gas_used,
672            status,
673        }
674    }
675
676    pub fn events(&self) -> &[ContractEvent] { &self.events }
677
678    pub fn gas_used(&self) -> u64 { self.gas_used }
679
680    pub fn status(&self) -> &TransactionStatus { &self.status }
681}
682
683/// `TransactionInfo` is the object we store in the transaction accumulator. It
684/// consists of the transaction as well as the execution result of this
685/// transaction.
686#[derive(
687    Clone,
688    CryptoHasher,
689    BCSCryptoHash,
690    Debug,
691    Eq,
692    PartialEq,
693    Serialize,
694    Deserialize,
695)]
696#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
697pub struct TransactionInfo {
698    /// The hash of this transaction.
699    transaction_hash: HashValue,
700
701    /// The root hash of Sparse Merkle Tree describing the world state at the
702    /// end of this transaction.
703    state_root_hash: HashValue,
704
705    /// The root hash of Merkle Accumulator storing all events emitted during
706    /// this transaction.
707    event_root_hash: HashValue,
708
709    /// The amount of gas used.
710    gas_used: u64,
711
712    /// The vm status. If it is not `Executed`, this will provide the general
713    /// error class. Execution failures and Move abort's recieve more
714    /// detailed information. But other errors are generally categorized
715    /// with no status code or other information
716    status: KeptVMStatus,
717}
718
719impl TransactionInfo {
720    /// Constructs a new `TransactionInfo` object using transaction hash, state
721    /// root hash and event root hash.
722    pub fn new(
723        transaction_hash: HashValue, state_root_hash: HashValue,
724        event_root_hash: HashValue, gas_used: u64, status: KeptVMStatus,
725    ) -> TransactionInfo {
726        TransactionInfo {
727            transaction_hash,
728            state_root_hash,
729            event_root_hash,
730            gas_used,
731            status,
732        }
733    }
734
735    /// Returns the hash of this transaction.
736    pub fn transaction_hash(&self) -> HashValue { self.transaction_hash }
737
738    /// Returns root hash of Sparse Merkle Tree describing the world state at
739    /// the end of this transaction.
740    pub fn state_root_hash(&self) -> HashValue { self.state_root_hash }
741
742    /// Returns the root hash of Merkle Accumulator storing all events emitted
743    /// during this transaction.
744    pub fn event_root_hash(&self) -> HashValue { self.event_root_hash }
745
746    /// Returns the amount of gas used by this transaction.
747    pub fn gas_used(&self) -> u64 { self.gas_used }
748
749    pub fn status(&self) -> &KeptVMStatus { &self.status }
750}
751
752impl Display for TransactionInfo {
753    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
754        write!(
755            f,
756            "TransactionInfo: [txn_hash: {}, state_root_hash: {}, event_root_hash: {}, gas_used: {}, recorded_status: {:?}]",
757            self.transaction_hash(), self.state_root_hash(), self.event_root_hash(), self.gas_used(), self.status(),
758        )
759    }
760}
761
762#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
763pub struct TransactionToCommit {
764    transaction: Transaction,
765    events: Vec<ContractEvent>,
766    gas_used: u64,
767    status: KeptVMStatus,
768}
769
770impl TransactionToCommit {
771    pub fn new(
772        transaction: Transaction, events: Vec<ContractEvent>, gas_used: u64,
773        status: KeptVMStatus,
774    ) -> Self {
775        TransactionToCommit {
776            transaction,
777            events,
778            gas_used,
779            status,
780        }
781    }
782
783    pub fn transaction(&self) -> &Transaction { &self.transaction }
784
785    pub fn events(&self) -> &[ContractEvent] { &self.events }
786
787    pub fn gas_used(&self) -> u64 { self.gas_used }
788
789    pub fn status(&self) -> &KeptVMStatus { &self.status }
790}
791
792/// `Transaction` will be the transaction type used internally in the diem node
793/// to represent the transaction to be processed and persisted.
794///
795/// We suppress the clippy warning here as we would expect most of the
796/// transaction to be user transaction.
797#[allow(clippy::large_enum_variant)]
798#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
799#[derive(
800    Clone,
801    Debug,
802    Eq,
803    PartialEq,
804    Serialize,
805    Deserialize,
806    CryptoHasher,
807    BCSCryptoHash,
808)]
809pub enum Transaction {
810    /// Transaction submitted by the user. e.g: P2P payment transaction,
811    /// publishing module transaction, etc.
812    /// TODO: We need to rename SignedTransaction to SignedUserTransaction, as
813    /// well as all the other       transaction types we had in our
814    /// codebase.
815    UserTransaction(SignedTransaction),
816
817    /// Genesis transaction carrying the epoch-change event for the
818    /// initial validator set.
819    GenesisTransaction(Vec<ContractEvent>),
820
821    /// Transaction to update the block metadata resource at the beginning
822    /// of a block.
823    BlockMetadata(BlockMetadata),
824}
825
826#[derive(Deserialize)]
827pub enum TransactionUnchecked {
828    UserTransaction(SignedTransactionUnchecked),
829    GenesisTransaction(Vec<ContractEvent>),
830    BlockMetadata(BlockMetadata),
831}
832
833impl From<TransactionUnchecked> for Transaction {
834    fn from(t: TransactionUnchecked) -> Self {
835        match t {
836            TransactionUnchecked::UserTransaction(t) => {
837                Self::UserTransaction(t.into())
838            }
839            TransactionUnchecked::GenesisTransaction(t) => {
840                Self::GenesisTransaction(t)
841            }
842            TransactionUnchecked::BlockMetadata(t) => Self::BlockMetadata(t),
843        }
844    }
845}
846
847impl Transaction {
848    pub fn as_signed_user_txn(&self) -> Result<&SignedTransaction> {
849        match self {
850            Transaction::UserTransaction(txn) => Ok(txn),
851            _ => Err(format_err!("Not a user transaction.")),
852        }
853    }
854}
855
856impl TryFrom<Transaction> for SignedTransaction {
857    type Error = Error;
858
859    fn try_from(txn: Transaction) -> Result<Self> {
860        match txn {
861            Transaction::UserTransaction(txn) => Ok(txn),
862            _ => Err(format_err!("Not a user transaction.")),
863        }
864    }
865}