1use 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; pub const PRE_GENESIS_VERSION: Version = u64::max_value();
80
81#[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: AccountAddress,
96
97 payload: TransactionPayload,
99
100 expiration_timestamp_secs: u64,
106
107 chain_id: ChainId,
109}
110
111impl RawTransaction {
112 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 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 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 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 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 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 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 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 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 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 expiration_timestamp_secs: u64::max_value(),
320 chain_id: Default::default(),
321 })
322 }
323
324 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 pub fn sender(&self) -> AccountAddress { self.sender }
398}
399
400#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
402pub enum TransactionPayload {
403 WriteSet(WriteSetPayload),
405 Script(Script),
407 Module(Module),
409 ScriptFunction(ScriptFunction),
412
413 Election(ElectionPayload),
415
416 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 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#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
561pub enum WriteSetPayload {
562 Direct(ChangeSet),
564 Script {
566 execute_as: AccountAddress,
568 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#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
592pub struct SignedTransaction {
593 raw_txn: RawTransaction,
595
596 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#[derive(Clone, Debug, Eq, PartialEq, Hash)]
618pub struct SignatureCheckedTransaction(SignedTransaction);
619
620impl SignatureCheckedTransaction {
621 pub fn into_inner(self) -> SignedTransaction { self.0 }
623
624 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 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 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#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
810pub enum TransactionStatus {
811 Discard(DiscardedVMStatus),
813
814 Keep(KeptVMStatus),
816
817 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 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#[derive(Clone, Debug, Eq, PartialEq)]
892pub struct VMValidatorResult {
893 status: Option<DiscardedVMStatus>,
897
898 score: u64,
902
903 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#[derive(Clone, Debug, Eq, PartialEq)]
943pub struct TransactionOutput {
944 write_set: WriteSet,
946
947 events: Vec<ContractEvent>,
949
950 gas_used: u64,
952
953 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#[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 transaction_hash: HashValue,
1000
1001 state_root_hash: HashValue,
1004
1005 event_root_hash: HashValue,
1008
1009 gas_used: u64,
1011
1012 status: KeptVMStatus,
1017}
1018
1019impl TransactionInfo {
1020 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 pub fn transaction_hash(&self) -> HashValue { self.transaction_hash }
1037
1038 pub fn state_root_hash(&self) -> HashValue { self.state_root_hash }
1041
1042 pub fn event_root_hash(&self) -> HashValue { self.event_root_hash }
1045
1046 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#[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 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 pub fn new_empty() -> Self {
1131 Self::new(vec![], None, None, TransactionListProof::new_empty())
1132 }
1133
1134 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 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#[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 UserTransaction(SignedTransaction),
1223
1224 GenesisTransaction(WriteSetPayload),
1227
1228 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 Transaction::GenesisTransaction(_write_set) => {
1271 String::from("genesis")
1272 }
1273 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}