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    collections::HashMap,
10    convert::TryFrom,
11    fmt::{self, Display, Formatter},
12    ops::Deref,
13};
14
15use anyhow::{ensure, format_err, Error, Result};
16#[cfg(any(test, feature = "fuzzing"))]
17use proptest_derive::Arbitrary;
18use serde::{Deserialize, Serialize};
19
20pub use change_set::ChangeSet;
21use diem_crypto::{
22    hash::{CryptoHash, EventAccumulatorHasher},
23    traits::SigningKey,
24    HashValue, PrivateKey, VRFProof,
25};
26use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
27pub use module::Module;
28use move_core_types::transaction_argument::convert_txn_args;
29use pow_types::StakingEvent;
30pub use script::{
31    ArgumentABI, Script, ScriptABI, ScriptFunction, ScriptFunctionABI,
32    TransactionScriptABI, TypeArgumentABI,
33};
34pub use transaction_argument::{
35    parse_transaction_argument, TransactionArgument,
36};
37
38use crate::{
39    account_address::AccountAddress,
40    account_state_blob::AccountStateBlob,
41    block_info::PivotBlockDecision,
42    block_metadata::BlockMetadata,
43    chain_id::ChainId,
44    contract_event::ContractEvent,
45    ledger_info::LedgerInfo,
46    proof::{
47        accumulator::InMemoryAccumulator, TransactionInfoWithProof,
48        TransactionListProof,
49    },
50    term_state::{
51        DisputeEvent, ElectionEvent, NodeID, RegisterEvent, RetireEvent,
52        UpdateVotingPowerEvent,
53    },
54    transaction::authenticator::{
55        TransactionAuthenticator, TransactionAuthenticatorUnchecked,
56    },
57    validator_config::{
58        ConsensusPrivateKey, ConsensusPublicKey, ConsensusSignature,
59        ConsensusVRFProof, ConsensusVRFPublicKey, MultiConsensusSignature,
60    },
61    vm_status::{
62        DiscardedVMStatus, KeptVMStatus, StatusCode, StatusType, VMStatus,
63    },
64    write_set::WriteSet,
65};
66
67pub mod authenticator;
68mod change_set;
69pub mod helpers;
70pub mod metadata;
71mod module;
72mod script;
73mod transaction_argument;
74
75pub type Version = u64; // Height - also used for MVCC in StateDB
76
77// In StateDB, things readable by the genesis transaction are under this
78// version.
79pub const PRE_GENESIS_VERSION: Version = u64::max_value();
80
81/// RawTransaction is the portion of a transaction that a client signs.
82#[derive(
83    Clone,
84    Debug,
85    Hash,
86    Eq,
87    PartialEq,
88    Serialize,
89    Deserialize,
90    CryptoHasher,
91    BCSCryptoHash,
92)]
93pub struct RawTransaction {
94    /// Sender's address.
95    sender: AccountAddress,
96
97    /// The transaction payload, e.g., a script to execute.
98    payload: TransactionPayload,
99
100    /// Expiration timestamp for this transaction, represented
101    /// as seconds from the Unix Epoch. If the current blockchain timestamp
102    /// is greater than or equal to this time, then the transaction has
103    /// expired and will be discarded. This can be set to a large value far
104    /// in the future to indicate that a transaction does not expire.
105    expiration_timestamp_secs: u64,
106
107    /// Chain ID of the Diem network this transaction is intended for.
108    chain_id: ChainId,
109}
110
111impl RawTransaction {
112    /// Create a new `RawTransaction` with a payload.
113    ///
114    /// It can be either to publish a module, to execute a script, or to issue a
115    /// writeset transaction.
116    pub fn new(
117        sender: AccountAddress, payload: TransactionPayload,
118        expiration_timestamp_secs: u64, chain_id: ChainId,
119    ) -> Self {
120        RawTransaction {
121            sender,
122            payload,
123            expiration_timestamp_secs,
124            chain_id,
125        }
126    }
127
128    /// Create a new `RawTransaction` with a script.
129    ///
130    /// A script transaction contains only code to execute. No publishing is
131    /// allowed in scripts.
132    pub fn new_script(
133        sender: AccountAddress, script: Script, expiration_timestamp_secs: u64,
134        chain_id: ChainId,
135    ) -> Self {
136        RawTransaction {
137            sender,
138            payload: TransactionPayload::Script(script),
139            expiration_timestamp_secs,
140            chain_id,
141        }
142    }
143
144    /// Create a new `RawTransaction` with a script function.
145    ///
146    /// A script transaction contains only code to execute. No publishing is
147    /// allowed in scripts.
148    pub fn new_script_function(
149        sender: AccountAddress, script_function: ScriptFunction,
150        expiration_timestamp_secs: u64, chain_id: ChainId,
151    ) -> Self {
152        RawTransaction {
153            sender,
154            payload: TransactionPayload::ScriptFunction(script_function),
155            expiration_timestamp_secs,
156            chain_id,
157        }
158    }
159
160    /// Create a new `RawTransaction` with a module to publish.
161    ///
162    /// A module transaction is the only way to publish code. Only one module
163    /// per transaction can be published.
164    pub fn new_module(
165        sender: AccountAddress, module: Module, expiration_timestamp_secs: u64,
166        chain_id: ChainId,
167    ) -> Self {
168        RawTransaction {
169            sender,
170            payload: TransactionPayload::Module(module),
171            expiration_timestamp_secs,
172            chain_id,
173        }
174    }
175
176    pub fn new_write_set(
177        sender: AccountAddress, write_set: WriteSet, chain_id: ChainId,
178    ) -> Self {
179        Self::new_change_set(
180            sender,
181            ChangeSet::new(write_set, vec![]),
182            chain_id,
183        )
184    }
185
186    pub fn new_change_set(
187        sender: AccountAddress, change_set: ChangeSet, chain_id: ChainId,
188    ) -> Self {
189        RawTransaction {
190            sender,
191            payload: TransactionPayload::WriteSet(WriteSetPayload::Direct(
192                change_set,
193            )),
194            // Write-set transactions are special and important and shouldn't
195            // expire.
196            expiration_timestamp_secs: u64::max_value(),
197            chain_id,
198        }
199    }
200
201    pub fn new_writeset_script(
202        sender: AccountAddress, script: Script, signer: AccountAddress,
203        chain_id: ChainId,
204    ) -> Self {
205        RawTransaction {
206            sender,
207            payload: TransactionPayload::WriteSet(WriteSetPayload::Script {
208                execute_as: signer,
209                script,
210            }),
211            // Write-set transactions are special and important and shouldn't
212            // expire.
213            expiration_timestamp_secs: u64::max_value(),
214            chain_id,
215        }
216    }
217
218    pub fn new_pivot_decision(
219        sender: AccountAddress, pivot_decision: PivotBlockDecision,
220        chain_id: ChainId,
221    ) -> Self {
222        RawTransaction {
223            sender,
224            payload: TransactionPayload::PivotDecision(pivot_decision),
225            // Write-set transactions are special and important and shouldn't
226            // expire.
227            expiration_timestamp_secs: u64::max_value(),
228            chain_id,
229        }
230    }
231
232    pub fn new_election(
233        sender: AccountAddress, election_payload: ElectionPayload,
234        chain_id: ChainId,
235    ) -> Self {
236        RawTransaction {
237            sender,
238            payload: TransactionPayload::Election(election_payload),
239            // Write-set transactions are special and important and shouldn't
240            // expire.
241            expiration_timestamp_secs: u64::max_value(),
242            chain_id,
243        }
244    }
245
246    pub fn new_dispute(
247        sender: AccountAddress, dispute_payload: DisputePayload,
248    ) -> Self {
249        RawTransaction {
250            sender,
251            payload: TransactionPayload::Dispute(dispute_payload),
252            // Write-set transactions are special and important and shouldn't
253            // expire.
254            expiration_timestamp_secs: u64::max_value(),
255            chain_id: Default::default(),
256        }
257    }
258
259    pub fn new_retire(
260        sender: AccountAddress, retire_payload: RetirePayload,
261    ) -> Self {
262        RawTransaction {
263            sender,
264            payload: TransactionPayload::Retire(retire_payload),
265            // Write-set transactions are special and important and shouldn't
266            // expire.
267            expiration_timestamp_secs: u64::max_value(),
268            chain_id: Default::default(),
269        }
270    }
271
272    pub fn from_staking_event(
273        staking_event: &StakingEvent, sender: AccountAddress,
274    ) -> Result<Self> {
275        let payload = match staking_event {
276            StakingEvent::Register(
277                addr_h256,
278                bls_pub_key_bytes,
279                vrf_pub_key_bytes,
280            ) => {
281                let addr = AccountAddress::from_bytes(addr_h256)?;
282                let public_key =
283                    ConsensusPublicKey::try_from(bls_pub_key_bytes.as_slice())?;
284                let vrf_public_key = ConsensusVRFPublicKey::try_from(
285                    vrf_pub_key_bytes.as_slice(),
286                )?;
287                let node_id =
288                    NodeID::new(public_key.clone(), vrf_public_key.clone());
289                ensure!(
290                    node_id.addr == addr,
291                    "register event has unmatching address and keys"
292                );
293                TransactionPayload::Register(RegisterPayload {
294                    public_key,
295                    vrf_public_key,
296                })
297            }
298            StakingEvent::IncreaseStake(addr_h256, updated_voting_power) => {
299                let addr = AccountAddress::from_bytes(addr_h256)?;
300                TransactionPayload::UpdateVotingPower(
301                    UpdateVotingPowerPayload {
302                        node_address: addr,
303                        voting_power: *updated_voting_power,
304                    },
305                )
306            }
307            StakingEvent::Retire(identifier, votes) => {
308                TransactionPayload::Retire(RetirePayload {
309                    node_id: AccountAddress::new(identifier.0),
310                    votes: *votes,
311                })
312            }
313        };
314        Ok(RawTransaction {
315            sender,
316            payload,
317            // Write-set transactions are special and important and shouldn't
318            // expire.
319            expiration_timestamp_secs: u64::max_value(),
320            chain_id: Default::default(),
321        })
322    }
323
324    /// Signs the given `RawTransaction`. Note that this consumes the
325    /// `RawTransaction` and turns it into a `SignatureCheckedTransaction`.
326    ///
327    /// For a transaction that has just been signed, its signature is expected
328    /// to be valid.
329    pub fn sign(
330        self, private_key: &ConsensusPrivateKey,
331    ) -> Result<SignatureCheckedTransaction> {
332        let signature = match self.payload {
333            TransactionPayload::PivotDecision(ref pivot_decision) => {
334                private_key.sign(pivot_decision)
335            }
336            _ => private_key.sign(&self),
337        };
338        let public_key = private_key.public_key();
339        Ok(SignatureCheckedTransaction(SignedTransaction::new(
340            self, public_key, signature,
341        )))
342    }
343
344    pub fn into_payload(self) -> TransactionPayload { self.payload }
345
346    pub fn format_for_client(
347        &self, get_transaction_name: impl Fn(&[u8]) -> String,
348    ) -> String {
349        let (code, args) = match &self.payload {
350            TransactionPayload::WriteSet(_) => ("genesis".to_string(), vec![]),
351            TransactionPayload::Script(script) => (
352                get_transaction_name(script.code()),
353                convert_txn_args(script.args()),
354            ),
355            TransactionPayload::ScriptFunction(script_fn) => (
356                format!("{}::{}", script_fn.module(), script_fn.function()),
357                script_fn.args().to_vec(),
358            ),
359            TransactionPayload::Module(_) => {
360                ("module publishing".to_string(), vec![])
361            }
362            TransactionPayload::Election(_) => ("election".to_string(), vec![]),
363            TransactionPayload::Retire(_) => ("retire".to_string(), vec![]),
364            TransactionPayload::PivotDecision(_) => {
365                ("pivot_decision".to_string(), vec![])
366            }
367            TransactionPayload::Register(_) => ("register".to_string(), vec![]),
368            TransactionPayload::UpdateVotingPower(_) => {
369                ("update_voting_power".to_string(), vec![])
370            }
371            TransactionPayload::Dispute(_) => ("dispute".to_string(), vec![]),
372        };
373        let mut f_args: String = "".to_string();
374        for arg in args {
375            f_args = format!("{}\n\t\t\t{:02X?},", f_args, arg);
376        }
377        format!(
378            "RawTransaction {{ \n\
379             \tsender: {}, \n\
380             \tpayload: {{, \n\
381             \t\ttransaction: {}, \n\
382             \t\targs: [ {} \n\
383             \t\t]\n\
384             \t}}, \n\
385             \texpiration_timestamp_secs: {:#?}, \n\
386             \tchain_id: {},
387             }}",
388            self.sender,
389            code,
390            f_args,
391            self.expiration_timestamp_secs,
392            self.chain_id,
393        )
394    }
395
396    /// Return the sender of this transaction.
397    pub fn sender(&self) -> AccountAddress { self.sender }
398}
399
400/// Different kinds of transactions.
401#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
402pub enum TransactionPayload {
403    /// A system maintenance transaction.
404    WriteSet(WriteSetPayload),
405    /// A transaction that executes code.
406    Script(Script),
407    /// A transaction that publishes code.
408    Module(Module),
409    /// A transaction that executes an existing script function published
410    /// on-chain.
411    ScriptFunction(ScriptFunction),
412
413    /// A transaction that add a node to committee candidates.
414    Election(ElectionPayload),
415
416    /// A transaction that sets a node to `Retire` status so the node will not
417    /// be elected.
418    Retire(RetirePayload),
419
420    Register(RegisterPayload),
421
422    UpdateVotingPower(UpdateVotingPowerPayload),
423
424    PivotDecision(PivotBlockDecision),
425
426    Dispute(DisputePayload),
427}
428
429impl TransactionPayload {
430    pub fn should_trigger_reconfiguration_by_default(&self) -> bool {
431        match self {
432            Self::WriteSet(ws) => {
433                ws.should_trigger_reconfiguration_by_default()
434            }
435            _ => false,
436        }
437    }
438
439    pub fn into_script_function(self) -> ScriptFunction {
440        match self {
441            Self::ScriptFunction(f) => f,
442            payload => panic!(
443                "Expected ScriptFunction(_) payload, found: {:#?}",
444                payload
445            ),
446        }
447    }
448}
449
450#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
451#[serde(rename_all = "camelCase")]
452pub struct ElectionPayload {
453    pub public_key: ConsensusPublicKey,
454    pub vrf_public_key: ConsensusVRFPublicKey,
455    pub target_term: u64,
456    pub vrf_proof: ConsensusVRFProof,
457}
458
459impl ElectionPayload {
460    pub fn to_event(&self) -> ContractEvent {
461        let event = ElectionEvent::new(
462            self.public_key.clone(),
463            self.vrf_public_key.clone(),
464            self.vrf_proof.to_hash().unwrap(),
465            self.target_term,
466        );
467        ContractEvent::new(
468            ElectionEvent::event_key(),
469            bcs::to_bytes(&event).unwrap(),
470        )
471    }
472}
473
474#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
475#[serde(rename_all = "camelCase")]
476pub struct RetirePayload {
477    pub node_id: AccountAddress,
478    pub votes: u64,
479}
480
481impl RetirePayload {
482    pub fn to_event(&self) -> ContractEvent {
483        let event = RetireEvent::new(self.node_id, self.votes);
484        ContractEvent::new(
485            RetireEvent::event_key(),
486            bcs::to_bytes(&event).unwrap(),
487        )
488    }
489}
490
491#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
492#[serde(rename_all = "camelCase")]
493pub struct RegisterPayload {
494    pub public_key: ConsensusPublicKey,
495    pub vrf_public_key: ConsensusVRFPublicKey,
496}
497
498impl RegisterPayload {
499    pub fn to_event(&self) -> ContractEvent {
500        let event = RegisterEvent::new(
501            self.public_key.clone(),
502            self.vrf_public_key.clone(),
503        );
504        ContractEvent::new(
505            RegisterEvent::event_key(),
506            bcs::to_bytes(&event).unwrap(),
507        )
508    }
509}
510
511#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
512#[serde(rename_all = "camelCase")]
513pub struct UpdateVotingPowerPayload {
514    pub node_address: AccountAddress,
515    pub voting_power: u64,
516}
517
518impl UpdateVotingPowerPayload {
519    pub fn to_event(&self) -> ContractEvent {
520        let event = UpdateVotingPowerEvent::new(
521            self.node_address.clone(),
522            self.voting_power,
523        );
524        ContractEvent::new(
525            UpdateVotingPowerEvent::event_key(),
526            bcs::to_bytes(&event).unwrap(),
527        )
528    }
529}
530
531#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
532#[serde(rename_all = "camelCase")]
533pub struct DisputePayload {
534    pub address: AccountAddress,
535    pub bls_pub_key: ConsensusPublicKey,
536    pub vrf_pub_key: ConsensusVRFPublicKey,
537    pub conflicting_votes: ConflictSignature,
538}
539
540#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
541pub enum ConflictSignature {
542    // Use raw bytes instead of `Proposal` or `Vote` to avoid dependency loop.
543    Proposal((Vec<u8>, Vec<u8>)),
544    Vote((Vec<u8>, Vec<u8>)),
545}
546
547impl DisputePayload {
548    pub fn to_event(&self) -> ContractEvent {
549        let event = DisputeEvent {
550            node_id: self.address,
551        };
552        ContractEvent::new(
553            DisputeEvent::event_key(),
554            bcs::to_bytes(&event).unwrap(),
555        )
556    }
557}
558
559/// Two different kinds of WriteSet transactions.
560#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
561pub enum WriteSetPayload {
562    /// Directly passing in the WriteSet.
563    Direct(ChangeSet),
564    /// Generate the WriteSet by running a script.
565    Script {
566        /// Execute the script as the designated signer.
567        execute_as: AccountAddress,
568        /// Script body that gets executed.
569        script: Script,
570    },
571}
572
573impl WriteSetPayload {
574    pub fn should_trigger_reconfiguration_by_default(&self) -> bool {
575        match self {
576            Self::Direct(_) => true,
577            Self::Script { .. } => false,
578        }
579    }
580}
581
582/// A transaction that has been signed.
583///
584/// A `SignedTransaction` is a single transaction that can be atomically
585/// executed. Clients submit these to validator nodes, and the validator and
586/// executor submits these to the VM.
587///
588/// **IMPORTANT:** The signature of a `SignedTransaction` is not guaranteed to
589/// be verified. For a transaction whose signature is statically guaranteed to
590/// be verified, see [`SignatureCheckedTransaction`].
591#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
592pub struct SignedTransaction {
593    /// The raw transaction
594    raw_txn: RawTransaction,
595
596    /// Public key and signature to authenticate
597    authenticator: TransactionAuthenticator,
598}
599
600#[derive(Deserialize)]
601pub struct SignedTransactionUnchecked {
602    pub raw_txn: RawTransaction,
603    pub authenticator: TransactionAuthenticatorUnchecked,
604}
605
606impl From<SignedTransactionUnchecked> for SignedTransaction {
607    fn from(t: SignedTransactionUnchecked) -> Self {
608        Self {
609            raw_txn: t.raw_txn,
610            authenticator: t.authenticator.into(),
611        }
612    }
613}
614
615/// A transaction for which the signature has been verified. Created by
616/// [`SignedTransaction::check_signature`] and [`RawTransaction::sign`].
617#[derive(Clone, Debug, Eq, PartialEq, Hash)]
618pub struct SignatureCheckedTransaction(SignedTransaction);
619
620impl SignatureCheckedTransaction {
621    /// Returns the `SignedTransaction` within.
622    pub fn into_inner(self) -> SignedTransaction { self.0 }
623
624    /// Returns the `RawTransaction` within.
625    pub fn into_raw_transaction(self) -> RawTransaction {
626        self.0.into_raw_transaction()
627    }
628}
629
630impl Deref for SignatureCheckedTransaction {
631    type Target = SignedTransaction;
632
633    fn deref(&self) -> &Self::Target { &self.0 }
634}
635
636impl fmt::Debug for SignedTransaction {
637    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638        write!(
639            f,
640            "SignedTransaction {{ \n \
641             {{ raw_txn: {:#?}, \n \
642             authenticator: {:#?}, \n \
643             }} \n \
644             }}",
645            self.raw_txn, self.authenticator
646        )
647    }
648}
649
650impl SignedTransaction {
651    pub fn new(
652        raw_txn: RawTransaction, public_key: ConsensusPublicKey,
653        signature: ConsensusSignature,
654    ) -> SignedTransaction {
655        let authenticator =
656            TransactionAuthenticator::bls(public_key, signature);
657        SignedTransaction {
658            raw_txn,
659            authenticator,
660        }
661    }
662
663    pub fn new_multisig(
664        raw_txn: RawTransaction, signatures: Vec<(ConsensusSignature, usize)>,
665    ) -> SignedTransaction {
666        let signature = MultiConsensusSignature::new(signatures).unwrap();
667        let authenticator = TransactionAuthenticator::multi_bls(signature);
668        SignedTransaction {
669            raw_txn,
670            authenticator,
671        }
672    }
673
674    pub fn authenticator(&self) -> TransactionAuthenticator {
675        self.authenticator.clone()
676    }
677
678    pub fn raw_txn(&self) -> RawTransaction { self.raw_txn.clone() }
679
680    pub fn hash(&self) -> HashValue { self.raw_txn.hash() }
681
682    pub fn sender(&self) -> AccountAddress { self.raw_txn.sender }
683
684    pub fn into_raw_transaction(self) -> RawTransaction { self.raw_txn }
685
686    pub fn chain_id(&self) -> ChainId { self.raw_txn.chain_id }
687
688    pub fn payload(&self) -> &TransactionPayload { &self.raw_txn.payload }
689
690    pub fn expiration_timestamp_secs(&self) -> u64 {
691        self.raw_txn.expiration_timestamp_secs
692    }
693
694    pub fn raw_txn_bytes_len(&self) -> usize {
695        bcs::to_bytes(&self.raw_txn)
696            .expect("Unable to serialize RawTransaction")
697            .len()
698    }
699
700    /// Checks that the signature of given transaction. Returns
701    /// `Ok(SignatureCheckedTransaction)` if the signature is valid.
702    pub fn check_signature(self) -> Result<SignatureCheckedTransaction> {
703        match self.payload() {
704            TransactionPayload::PivotDecision(pivot_decision) => {
705                self.authenticator.verify(pivot_decision)?
706            }
707            _ => self.authenticator.verify(&self.raw_txn)?,
708        }
709        Ok(SignatureCheckedTransaction(self))
710    }
711
712    pub fn format_for_client(
713        &self, get_transaction_name: impl Fn(&[u8]) -> String,
714    ) -> String {
715        format!(
716            "SignedTransaction {{ \n \
717             raw_txn: {}, \n \
718             authenticator: {:#?}, \n \
719             }}",
720            self.raw_txn.format_for_client(get_transaction_name),
721            self.authenticator
722        )
723    }
724}
725
726#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
727#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
728pub struct TransactionWithProof {
729    pub version: Version,
730    pub transaction: Transaction,
731    pub events: Option<Vec<ContractEvent>>,
732    pub proof: TransactionInfoWithProof,
733}
734
735impl TransactionWithProof {
736    pub fn new(
737        version: Version, transaction: Transaction,
738        events: Option<Vec<ContractEvent>>, proof: TransactionInfoWithProof,
739    ) -> Self {
740        Self {
741            version,
742            transaction,
743            events,
744            proof,
745        }
746    }
747
748    /// Verifies the transaction with the proof, both carried by `self`.
749    ///
750    /// A few things are ensured if no error is raised:
751    ///   1. This transaction exists in the ledger represented by `ledger_info`.
752    ///   2. This transaction is a `UserTransaction`.
753    ///   3. And this user transaction has the same `version`, `sender`, and
754    /// `sequence_number` as      indicated by the parameter list. If any of
755    /// these parameter is unknown to the call site      that is supposed to
756    /// be informed via this struct, get it from the struct itself, such
757    ///      as version and sender.
758    pub fn verify_user_txn(
759        &self, ledger_info: &LedgerInfo, version: Version,
760        sender: AccountAddress,
761    ) -> Result<()> {
762        let signed_transaction = self.transaction.as_signed_user_txn()?;
763
764        ensure!(
765            self.version == version,
766            "Version ({}) is not expected ({}).",
767            self.version,
768            version,
769        );
770        ensure!(
771            signed_transaction.sender() == sender,
772            "Sender ({}) not expected ({}).",
773            signed_transaction.sender(),
774            sender,
775        );
776        let txn_hash = self.transaction.hash();
777        ensure!(
778            txn_hash == self.proof.transaction_info().transaction_hash,
779            "Transaction hash ({}) not expected ({}).",
780            txn_hash,
781            self.proof.transaction_info().transaction_hash,
782        );
783
784        if let Some(events) = &self.events {
785            let event_hashes: Vec<_> =
786                events.iter().map(ContractEvent::hash).collect();
787            let event_root_hash =
788                InMemoryAccumulator::<EventAccumulatorHasher>::from_leaves(
789                    &event_hashes[..],
790                )
791                .root_hash();
792            ensure!(
793                event_root_hash
794                    == self.proof.transaction_info().event_root_hash,
795                "Event root hash ({}) not expected ({}).",
796                event_root_hash,
797                self.proof.transaction_info().event_root_hash,
798            );
799        }
800
801        self.proof.verify(ledger_info, version)
802    }
803}
804
805/// The status of executing a transaction. The VM decides whether or not we
806/// should `Keep` the transaction output or `Discard` it based upon the
807/// execution of the transaction. We wrap these decisions around a `VMStatus`
808/// that provides more detail on the final execution state of the VM.
809#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
810pub enum TransactionStatus {
811    /// Discard the transaction output
812    Discard(DiscardedVMStatus),
813
814    /// Keep the transaction output
815    Keep(KeptVMStatus),
816
817    /// Retry the transaction, e.g., after a reconfiguration
818    Retry,
819}
820
821impl TransactionStatus {
822    pub fn status(&self) -> Result<KeptVMStatus, StatusCode> {
823        match self {
824            TransactionStatus::Keep(status) => Ok(status.clone()),
825            TransactionStatus::Discard(code) => Err(*code),
826            TransactionStatus::Retry => {
827                Err(StatusCode::UNKNOWN_VALIDATION_STATUS)
828            }
829        }
830    }
831
832    pub fn is_discarded(&self) -> bool {
833        match self {
834            TransactionStatus::Discard(_) => true,
835            TransactionStatus::Keep(_) => false,
836            TransactionStatus::Retry => true,
837        }
838    }
839}
840
841impl From<VMStatus> for TransactionStatus {
842    fn from(vm_status: VMStatus) -> Self {
843        match vm_status.keep_or_discard() {
844            Ok(recorded) => TransactionStatus::Keep(recorded),
845            Err(code) => TransactionStatus::Discard(code),
846        }
847    }
848}
849
850#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
851pub enum GovernanceRole {
852    DiemRoot,
853    TreasuryCompliance,
854    Validator,
855    ValidatorOperator,
856    DesignatedDealer,
857    NonGovernanceRole,
858}
859
860impl GovernanceRole {
861    pub fn from_role_id(role_id: u64) -> Self {
862        use GovernanceRole::*;
863        match role_id {
864            0 => DiemRoot,
865            1 => TreasuryCompliance,
866            2 => DesignatedDealer,
867            3 => Validator,
868            4 => ValidatorOperator,
869            _ => NonGovernanceRole,
870        }
871    }
872
873    /// The higher the number that is returned, the greater priority assigned to
874    /// a transaction sent from an account with that role in mempool. All
875    /// transactions sent from an account with role priority N are ranked
876    /// higher than all transactions sent from accounts with role priorities <
877    /// N. Transactions from accounts with equal priority are ranked base on
878    /// other characteristics (e.g., gas price).
879    pub fn priority(&self) -> u64 {
880        use GovernanceRole::*;
881        match self {
882            DiemRoot => 3,
883            TreasuryCompliance => 2,
884            Validator | ValidatorOperator | DesignatedDealer => 1,
885            NonGovernanceRole => 0,
886        }
887    }
888}
889
890/// The result of running the transaction through the VM validator.
891#[derive(Clone, Debug, Eq, PartialEq)]
892pub struct VMValidatorResult {
893    /// Result of the validation: `None` if the transaction was successfully
894    /// validated or `Some(DiscardedVMStatus)` if the transaction should be
895    /// discarded.
896    status: Option<DiscardedVMStatus>,
897
898    /// Score for ranking the transaction priority (e.g., based on the gas
899    /// price). Only used when the status is `None`. Higher values indicate
900    /// a higher priority.
901    score: u64,
902
903    /// The account role for the transaction sender, so that certain
904    /// governance transactions can be prioritized above normal transactions.
905    /// Only used when the status is `None`.
906    governance_role: GovernanceRole,
907}
908
909impl VMValidatorResult {
910    pub fn new(
911        vm_status: Option<DiscardedVMStatus>, score: u64,
912        governance_role: GovernanceRole,
913    ) -> Self {
914        debug_assert!(
915            match vm_status {
916                None => true,
917                Some(status) => {
918                    status.status_type() == StatusType::Unknown
919                        || status.status_type() == StatusType::Validation
920                        || status.status_type()
921                            == StatusType::InvariantViolation
922                }
923            },
924            "Unexpected discarded status: {:?}",
925            vm_status
926        );
927        Self {
928            status: vm_status,
929            score,
930            governance_role,
931        }
932    }
933
934    pub fn status(&self) -> Option<DiscardedVMStatus> { self.status }
935
936    pub fn score(&self) -> u64 { self.score }
937
938    pub fn governance_role(&self) -> GovernanceRole { self.governance_role }
939}
940
941/// The output of executing a transaction.
942#[derive(Clone, Debug, Eq, PartialEq)]
943pub struct TransactionOutput {
944    /// The list of writes this transaction intends to do.
945    write_set: WriteSet,
946
947    /// The list of events emitted during this transaction.
948    events: Vec<ContractEvent>,
949
950    /// The amount of gas used during execution.
951    gas_used: u64,
952
953    /// The execution status.
954    status: TransactionStatus,
955}
956
957impl TransactionOutput {
958    pub fn new(
959        write_set: WriteSet, events: Vec<ContractEvent>, gas_used: u64,
960        status: TransactionStatus,
961    ) -> Self {
962        TransactionOutput {
963            write_set,
964            events,
965            gas_used,
966            status,
967        }
968    }
969
970    pub fn into(self) -> (WriteSet, Vec<ContractEvent>) {
971        (self.write_set, self.events)
972    }
973
974    pub fn write_set(&self) -> &WriteSet { &self.write_set }
975
976    pub fn events(&self) -> &[ContractEvent] { &self.events }
977
978    pub fn gas_used(&self) -> u64 { self.gas_used }
979
980    pub fn status(&self) -> &TransactionStatus { &self.status }
981}
982
983/// `TransactionInfo` is the object we store in the transaction accumulator. It
984/// consists of the transaction as well as the execution result of this
985/// transaction.
986#[derive(
987    Clone,
988    CryptoHasher,
989    BCSCryptoHash,
990    Debug,
991    Eq,
992    PartialEq,
993    Serialize,
994    Deserialize,
995)]
996#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
997pub struct TransactionInfo {
998    /// The hash of this transaction.
999    transaction_hash: HashValue,
1000
1001    /// The root hash of Sparse Merkle Tree describing the world state at the
1002    /// end of this transaction.
1003    state_root_hash: HashValue,
1004
1005    /// The root hash of Merkle Accumulator storing all events emitted during
1006    /// this transaction.
1007    event_root_hash: HashValue,
1008
1009    /// The amount of gas used.
1010    gas_used: u64,
1011
1012    /// The vm status. If it is not `Executed`, this will provide the general
1013    /// error class. Execution failures and Move abort's recieve more
1014    /// detailed information. But other errors are generally categorized
1015    /// with no status code or other information
1016    status: KeptVMStatus,
1017}
1018
1019impl TransactionInfo {
1020    /// Constructs a new `TransactionInfo` object using transaction hash, state
1021    /// root hash and event root hash.
1022    pub fn new(
1023        transaction_hash: HashValue, state_root_hash: HashValue,
1024        event_root_hash: HashValue, gas_used: u64, status: KeptVMStatus,
1025    ) -> TransactionInfo {
1026        TransactionInfo {
1027            transaction_hash,
1028            state_root_hash,
1029            event_root_hash,
1030            gas_used,
1031            status,
1032        }
1033    }
1034
1035    /// Returns the hash of this transaction.
1036    pub fn transaction_hash(&self) -> HashValue { self.transaction_hash }
1037
1038    /// Returns root hash of Sparse Merkle Tree describing the world state at
1039    /// the end of this transaction.
1040    pub fn state_root_hash(&self) -> HashValue { self.state_root_hash }
1041
1042    /// Returns the root hash of Merkle Accumulator storing all events emitted
1043    /// during this transaction.
1044    pub fn event_root_hash(&self) -> HashValue { self.event_root_hash }
1045
1046    /// Returns the amount of gas used by this transaction.
1047    pub fn gas_used(&self) -> u64 { self.gas_used }
1048
1049    pub fn status(&self) -> &KeptVMStatus { &self.status }
1050}
1051
1052impl Display for TransactionInfo {
1053    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1054        write!(
1055            f,
1056            "TransactionInfo: [txn_hash: {}, state_root_hash: {}, event_root_hash: {}, gas_used: {}, recorded_status: {:?}]",
1057            self.transaction_hash(), self.state_root_hash(), self.event_root_hash(), self.gas_used(), self.status(),
1058        )
1059    }
1060}
1061
1062#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
1063pub struct TransactionToCommit {
1064    transaction: Transaction,
1065    account_states: HashMap<AccountAddress, AccountStateBlob>,
1066    events: Vec<ContractEvent>,
1067    gas_used: u64,
1068    status: KeptVMStatus,
1069}
1070
1071impl TransactionToCommit {
1072    pub fn new(
1073        transaction: Transaction,
1074        account_states: HashMap<AccountAddress, AccountStateBlob>,
1075        events: Vec<ContractEvent>, gas_used: u64, status: KeptVMStatus,
1076    ) -> Self {
1077        TransactionToCommit {
1078            transaction,
1079            account_states,
1080            events,
1081            gas_used,
1082            status,
1083        }
1084    }
1085
1086    pub fn transaction(&self) -> &Transaction { &self.transaction }
1087
1088    pub fn account_states(&self) -> &HashMap<AccountAddress, AccountStateBlob> {
1089        &self.account_states
1090    }
1091
1092    pub fn events(&self) -> &[ContractEvent] { &self.events }
1093
1094    pub fn gas_used(&self) -> u64 { self.gas_used }
1095
1096    pub fn status(&self) -> &KeptVMStatus { &self.status }
1097}
1098
1099/// The list may have three states:
1100/// 1. The list is empty. Both proofs must be `None`.
1101/// 2. The list has only 1 transaction/transaction_info. Then
1102/// `proof_of_first_transaction` must exist and `proof_of_last_transaction` must
1103/// be `None`. 3. The list has 2+ transactions/transaction_infos. The both
1104/// proofs must exist.
1105#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
1106pub struct TransactionListWithProof {
1107    pub transactions: Vec<Transaction>,
1108    pub events: Option<Vec<Vec<ContractEvent>>>,
1109    pub first_transaction_version: Option<Version>,
1110    pub proof: TransactionListProof,
1111}
1112
1113impl TransactionListWithProof {
1114    /// Constructor.
1115    pub fn new(
1116        transactions: Vec<Transaction>,
1117        events: Option<Vec<Vec<ContractEvent>>>,
1118        first_transaction_version: Option<Version>,
1119        proof: TransactionListProof,
1120    ) -> Self {
1121        Self {
1122            transactions,
1123            events,
1124            first_transaction_version,
1125            proof,
1126        }
1127    }
1128
1129    /// Creates an empty transaction list.
1130    pub fn new_empty() -> Self {
1131        Self::new(vec![], None, None, TransactionListProof::new_empty())
1132    }
1133
1134    /// Verifies the transaction list with the proofs, both carried on `self`.
1135    ///
1136    /// Two things are ensured if no error is raised:
1137    ///   1. All the transactions exist on the ledger represented by
1138    /// `ledger_info`.   2. And the transactions in the list has consecutive
1139    /// versions starting from `first_transaction_version`. When
1140    /// `first_transaction_version` is None, ensures the list is empty.
1141    pub fn verify(
1142        &self, ledger_info: &LedgerInfo,
1143        first_transaction_version: Option<Version>,
1144    ) -> Result<()> {
1145        ensure!(
1146            self.first_transaction_version == first_transaction_version,
1147            "First transaction version ({}) not expected ({}).",
1148            Self::display_option_version(self.first_transaction_version),
1149            Self::display_option_version(first_transaction_version),
1150        );
1151
1152        let txn_hashes: Vec<_> =
1153            self.transactions.iter().map(CryptoHash::hash).collect();
1154        self.proof.verify(
1155            ledger_info,
1156            self.first_transaction_version,
1157            &txn_hashes,
1158        )?;
1159
1160        // Verify the events if they exist.
1161        if let Some(event_lists) = &self.events {
1162            ensure!(
1163                event_lists.len() == self.transactions.len(),
1164                "The length of event_lists ({}) does not match the number of transactions ({}).",
1165                event_lists.len(),
1166                self.transactions.len(),
1167            );
1168            itertools::zip_eq(event_lists, self.proof.transaction_infos())
1169                .map(|(events, txn_info)| {
1170                    let event_hashes: Vec<_> = events.iter().map(ContractEvent::hash).collect();
1171                    let event_root_hash =
1172                        InMemoryAccumulator::<EventAccumulatorHasher>::from_leaves(&event_hashes)
1173                            .root_hash();
1174                    ensure!(
1175                        event_root_hash == txn_info.event_root_hash(),
1176                        "Some event root hash calculated doesn't match that carried on the \
1177                         transaction info.",
1178                    );
1179                    Ok(())
1180                })
1181                .collect::<Result<Vec<_>>>()?;
1182        }
1183
1184        Ok(())
1185    }
1186
1187    pub fn is_empty(&self) -> bool { self.transactions.is_empty() }
1188
1189    pub fn len(&self) -> usize { self.transactions.len() }
1190
1191    fn display_option_version(version: Option<Version>) -> String {
1192        match version {
1193            Some(v) => format!("{}", v),
1194            None => String::from("absent"),
1195        }
1196    }
1197}
1198
1199/// `Transaction` will be the transaction type used internally in the diem node
1200/// to represent the transaction to be processed and persisted.
1201///
1202/// We suppress the clippy warning here as we would expect most of the
1203/// transaction to be user transaction.
1204#[allow(clippy::large_enum_variant)]
1205#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
1206#[derive(
1207    Clone,
1208    Debug,
1209    Eq,
1210    PartialEq,
1211    Serialize,
1212    Deserialize,
1213    CryptoHasher,
1214    BCSCryptoHash,
1215)]
1216pub enum Transaction {
1217    /// Transaction submitted by the user. e.g: P2P payment transaction,
1218    /// publishing module transaction, etc.
1219    /// TODO: We need to rename SignedTransaction to SignedUserTransaction, as
1220    /// well as all the other       transaction types we had in our
1221    /// codebase.
1222    UserTransaction(SignedTransaction),
1223
1224    /// Transaction that applies a WriteSet to the current storage, it's
1225    /// applied manually via db-bootstrapper.
1226    GenesisTransaction(WriteSetPayload),
1227
1228    /// Transaction to update the block metadata resource at the beginning of a
1229    /// block.
1230    BlockMetadata(BlockMetadata),
1231}
1232
1233#[derive(Deserialize)]
1234pub enum TransactionUnchecked {
1235    UserTransaction(SignedTransactionUnchecked),
1236    GenesisTransaction(WriteSetPayload),
1237    BlockMetadata(BlockMetadata),
1238}
1239
1240impl From<TransactionUnchecked> for Transaction {
1241    fn from(t: TransactionUnchecked) -> Self {
1242        match t {
1243            TransactionUnchecked::UserTransaction(t) => {
1244                Self::UserTransaction(t.into())
1245            }
1246            TransactionUnchecked::GenesisTransaction(t) => {
1247                Self::GenesisTransaction(t)
1248            }
1249            TransactionUnchecked::BlockMetadata(t) => Self::BlockMetadata(t),
1250        }
1251    }
1252}
1253
1254impl Transaction {
1255    pub fn as_signed_user_txn(&self) -> Result<&SignedTransaction> {
1256        match self {
1257            Transaction::UserTransaction(txn) => Ok(txn),
1258            _ => Err(format_err!("Not a user transaction.")),
1259        }
1260    }
1261
1262    pub fn format_for_client(
1263        &self, get_transaction_name: impl Fn(&[u8]) -> String,
1264    ) -> String {
1265        match self {
1266            Transaction::UserTransaction(user_txn) => {
1267                user_txn.format_for_client(get_transaction_name)
1268            }
1269            // TODO: display proper information for client
1270            Transaction::GenesisTransaction(_write_set) => {
1271                String::from("genesis")
1272            }
1273            // TODO: display proper information for client
1274            Transaction::BlockMetadata(_block_metadata) => {
1275                String::from("block_metadata")
1276            }
1277        }
1278    }
1279}
1280
1281impl TryFrom<Transaction> for SignedTransaction {
1282    type Error = Error;
1283
1284    fn try_from(txn: Transaction) -> Result<Self> {
1285        match txn {
1286            Transaction::UserTransaction(txn) => Ok(txn),
1287            _ => Err(format_err!("Not a user transaction.")),
1288        }
1289    }
1290}