1use crate::log_entry::LogEntry;
6use cfx_types::{Address, Bloom, Space, U256, U64};
7use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
8use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
9use rlp_derive::{RlpDecodable, RlpEncodable};
10
11pub const TRANSACTION_OUTCOME_SUCCESS: u8 = 0;
12pub const TRANSACTION_OUTCOME_EXCEPTION_WITH_NONCE_BUMPING: u8 = 1; pub const TRANSACTION_OUTCOME_EXCEPTION_WITHOUT_NONCE_BUMPING: u8 = 2; pub const EVM_SPACE_FAIL: u8 = 0;
16pub const EVM_SPACE_SUCCESS: u8 = 1;
17
18#[repr(u8)]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum TransactionStatus {
21 Success = 0,
22 Failure = 1,
23 Skipped = 2,
24}
25
26impl TransactionStatus {
27 fn into_u8(&self) -> u8 {
28 match self {
29 TransactionStatus::Success => 0,
30 TransactionStatus::Failure => 1,
31 TransactionStatus::Skipped => 2,
32 }
33 }
34}
35
36impl Encodable for TransactionStatus {
37 fn rlp_append(&self, s: &mut RlpStream) {
38 s.append_internal(&self.into_u8());
39 }
40}
41
42impl Decodable for TransactionStatus {
43 fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
44 match rlp.as_val::<u8>()? {
45 0 => Ok(TransactionStatus::Success),
46 1 => Ok(TransactionStatus::Failure),
47 2 => Ok(TransactionStatus::Skipped),
48 _ => Err(DecoderError::Custom("Unrecognized outcome status")),
49 }
50 }
51}
52
53impl Default for TransactionStatus {
54 fn default() -> Self { TransactionStatus::Success }
55}
56
57impl TransactionStatus {
58 pub fn in_space(&self, space: Space) -> u8 {
59 match (space, self) {
60 (Space::Native, TransactionStatus::Success) => {
62 TRANSACTION_OUTCOME_SUCCESS
63 }
64 (Space::Native, TransactionStatus::Failure) => {
65 TRANSACTION_OUTCOME_EXCEPTION_WITH_NONCE_BUMPING
66 }
67 (Space::Native, TransactionStatus::Skipped) => {
68 TRANSACTION_OUTCOME_EXCEPTION_WITHOUT_NONCE_BUMPING
69 }
70
71 (Space::Ethereum, TransactionStatus::Success) => EVM_SPACE_SUCCESS,
73 (Space::Ethereum, TransactionStatus::Failure) => EVM_SPACE_FAIL,
74 (Space::Ethereum, TransactionStatus::Skipped) => 0xff,
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, RlpDecodable, RlpEncodable)]
80pub struct StorageChange {
81 pub address: Address,
82 pub collaterals: U64,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Default)]
88pub struct SortedStorageChanges {
89 pub storage_collateralized: Vec<StorageChange>,
90 pub storage_released: Vec<StorageChange>,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Default)]
95pub struct Receipt {
96 pub accumulated_gas_used: U256,
99 pub gas_fee: U256,
101 pub gas_sponsor_paid: bool,
103 pub log_bloom: Bloom,
105 pub logs: Vec<LogEntry>,
107 pub outcome_status: TransactionStatus,
109 pub storage_sponsor_paid: bool,
111 pub storage_collateralized: Vec<StorageChange>,
112 pub storage_released: Vec<StorageChange>,
113 pub burnt_gas_fee: Option<U256>,
114}
115
116impl Encodable for Receipt {
117 fn rlp_append(&self, s: &mut RlpStream) {
118 let length = if self.burnt_gas_fee.is_none() { 9 } else { 10 };
119 s.begin_list(length)
120 .append(&self.accumulated_gas_used)
121 .append(&self.gas_fee)
122 .append(&self.gas_sponsor_paid)
123 .append(&self.log_bloom)
124 .append_list(&self.logs)
125 .append(&self.outcome_status)
126 .append(&self.storage_sponsor_paid)
127 .append_list(&self.storage_collateralized)
128 .append_list(&self.storage_released);
129 if let Some(burnt_gas_fee) = self.burnt_gas_fee {
130 s.append(&burnt_gas_fee);
131 }
132 }
133}
134
135impl Decodable for Receipt {
136 fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
137 let item_count = rlp.item_count()?;
138 if !matches!(item_count, 9..=10) {
139 return Err(DecoderError::RlpIncorrectListLen);
140 }
141 Ok(Receipt {
142 accumulated_gas_used: rlp.val_at(0)?,
143 gas_fee: rlp.val_at(1)?,
144 gas_sponsor_paid: rlp.val_at(2)?,
145 log_bloom: rlp.val_at(3)?,
146 logs: rlp.list_at(4)?,
147 outcome_status: rlp.val_at(5)?,
148 storage_sponsor_paid: rlp.val_at(6)?,
149 storage_collateralized: rlp.list_at(7)?,
150 storage_released: rlp.list_at(8)?,
151 burnt_gas_fee: if item_count == 9 {
152 None
153 } else {
154 Some(rlp.val_at(9)?)
155 },
156 })
157 }
158}
159
160impl Receipt {
161 pub fn new(
162 outcome: TransactionStatus, accumulated_gas_used: U256, gas_fee: U256,
163 gas_sponsor_paid: bool, logs: Vec<LogEntry>, log_bloom: Bloom,
164 storage_sponsor_paid: bool, storage_collateralized: Vec<StorageChange>,
165 storage_released: Vec<StorageChange>, burnt_gas_fee: Option<U256>,
166 ) -> Self {
167 Self {
168 accumulated_gas_used,
169 gas_fee,
170 gas_sponsor_paid,
171 log_bloom,
172 logs,
173 outcome_status: outcome,
174 storage_sponsor_paid,
175 storage_collateralized,
176 storage_released,
177 burnt_gas_fee,
178 }
179 }
180
181 pub fn tx_skipped(&self) -> bool {
182 self.outcome_status == TransactionStatus::Skipped
183 }
184
185 pub fn tx_success(&self) -> bool {
186 self.outcome_status == TransactionStatus::Success
187 }
188
189 pub fn accumulated_gas_used(&self) -> U256 { self.accumulated_gas_used }
190
191 pub fn logs(&self) -> &[LogEntry] { &self.logs }
192}
193
194impl MallocSizeOf for StorageChange {
195 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 }
196}
197
198impl MallocSizeOf for Receipt {
199 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
200 self.logs.size_of(ops)
201 + self.storage_released.size_of(ops)
202 + self.storage_released.size_of(ops)
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, RlpDecodable, RlpEncodable)]
208pub struct BlockReceipts {
209 pub receipts: Vec<Receipt>,
211 pub block_number: u64,
214 pub secondary_reward: U256,
216 pub tx_execution_error_messages: Vec<String>,
219}
220
221impl MallocSizeOf for BlockReceipts {
222 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
223 self.receipts.size_of(ops)
224 }
225}
226
227#[test]
228fn test_transaction_outcome_rlp() {
229 assert_eq!(rlp::encode(&TransactionStatus::Success), rlp::encode(&0u8));
230 assert_eq!(rlp::encode(&TransactionStatus::Failure), rlp::encode(&1u8));
231 assert_eq!(rlp::encode(&TransactionStatus::Skipped), rlp::encode(&2u8));
232}
233
234#[test]
235fn test_receipt_rlp_serde() {
236 let mut receipt = Receipt {
237 accumulated_gas_used: 189000.into(),
238 gas_fee: 60054.into(),
239 burnt_gas_fee: Some(30027.into()),
240 ..Default::default()
241 };
242 assert_eq!(receipt, Rlp::new(&receipt.rlp_bytes()).as_val().unwrap());
243
244 receipt.burnt_gas_fee = None;
245 assert_eq!(receipt, Rlp::new(&receipt.rlp_bytes()).as_val().unwrap());
246}