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