1use crate::rpc::types::{Log, RpcAddress};
6use cfx_addr::Network;
7use cfx_types::{
8 address_util::AddressUtil, Bloom, Space, SpaceMap, H256, U256, U64,
9};
10use cfx_util_macros::bail;
11use cfx_vm_types::{contract_address, CreateContractAddress};
12use primitives::{
13 receipt::{
14 Receipt as PrimitiveReceipt, StorageChange as PrimitiveStorageChange,
15 },
16 transaction::Action,
17 SignedTransaction as PrimitiveTransaction, Transaction, TransactionIndex,
18 TransactionStatus,
19};
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct StorageChange {
24 pub address: RpcAddress,
25 pub collaterals: U64,
26}
27
28impl StorageChange {
29 pub fn try_from(
30 sc: PrimitiveStorageChange, network: Network,
31 ) -> Result<Self, String> {
32 Ok(Self {
33 address: RpcAddress::try_from_h160(sc.address, network)?,
34 collaterals: sc.collaterals,
35 })
36 }
37}
38
39#[derive(Debug, Serialize, Clone, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct Receipt {
42 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
43 pub transaction_type: Option<U64>,
44 pub transaction_hash: H256,
46 pub index: U64,
48 pub block_hash: H256,
50 pub epoch_number: Option<U64>,
52 pub from: RpcAddress,
54 pub to: Option<RpcAddress>,
57 pub gas_used: U256,
59 #[serde(skip_serializing_if = "Option::is_none")]
62 pub accumulated_gas_used: Option<U256>,
63 pub gas_fee: U256,
65 pub effective_gas_price: U256,
66 pub contract_created: Option<RpcAddress>,
68 pub logs: Vec<Log>,
70 pub logs_bloom: Bloom,
72 pub state_root: H256,
74 pub outcome_status: U64,
76 pub tx_exec_error_msg: Option<String>,
80 pub gas_covered_by_sponsor: bool,
82 pub storage_covered_by_sponsor: bool,
84 pub storage_collateralized: U64,
86 pub storage_released: Vec<StorageChange>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub space: Option<Space>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub burnt_gas_fee: Option<U256>,
93}
94
95impl Receipt {
96 pub fn new(
97 transaction: PrimitiveTransaction, receipt: PrimitiveReceipt,
98 transaction_index: TransactionIndex, prior_gas_used: U256,
99 epoch_number: Option<u64>, block_number: u64,
100 maybe_base_price: Option<SpaceMap<U256>>,
101 maybe_state_root: Option<H256>, tx_exec_error_msg: Option<String>,
102 network: Network, include_eth_receipt: bool,
103 include_accumulated_gas_used: bool,
104 ) -> Result<Receipt, String> {
105 let PrimitiveReceipt {
106 accumulated_gas_used,
107 gas_fee,
108 gas_sponsor_paid,
109 log_bloom,
110 logs,
111 outcome_status,
112 storage_collateralized,
113 storage_released,
114 storage_sponsor_paid,
115 ..
116 } = receipt;
117
118 let (address, action, space) = match transaction.unsigned {
119 Transaction::Native(ref unsigned) => {
120 if Action::Create == unsigned.action()
121 && outcome_status == TransactionStatus::Success
122 {
123 let (mut created_address, _) = contract_address(
124 CreateContractAddress::FromSenderNonceAndCodeHash,
125 block_number.into(),
126 &transaction.sender,
127 unsigned.nonce(),
128 unsigned.data(),
129 );
130 created_address.set_contract_type_bits();
131 let address = Some(RpcAddress::try_from_h160(
132 created_address,
133 network,
134 )?);
135 (address, unsigned.action().clone(), Space::Native)
136 } else {
137 (None, unsigned.action().clone(), Space::Native)
138 }
139 }
140 Transaction::Ethereum(ref unsigned) => {
141 if include_eth_receipt {
142 if Action::Create == unsigned.action()
143 && outcome_status == TransactionStatus::Success
144 {
145 let (created_address, _) = contract_address(
146 CreateContractAddress::FromSenderNonce,
147 0,
148 &transaction.sender,
149 unsigned.nonce(),
150 unsigned.data(),
151 );
152 let address = Some(RpcAddress::try_from_h160(
153 created_address,
154 network,
155 )?);
156 (address, unsigned.action().clone(), Space::Ethereum)
157 } else {
158 (None, unsigned.action().clone(), Space::Ethereum)
159 }
160 } else {
161 bail!(format!("Does not support EIP-155 transaction in Conflux space RPC. get_receipt for tx: {:?}",transaction));
162 }
163 }
164 };
165
166 let storage_collateralized = storage_collateralized
169 .get(0)
170 .map(|sc| sc.collaterals)
171 .map(Into::into)
172 .unwrap_or_default();
173
174 let effective_gas_price = if let Some(base_price) = maybe_base_price {
175 let base_price = base_price[transaction.space()];
176 if *transaction.gas_price() < base_price {
177 *transaction.gas_price()
178 } else {
179 transaction.effective_gas_price(&base_price)
180 }
181 } else {
182 *transaction.gas_price()
183 };
184
185 Ok(Receipt {
186 transaction_type: Some(U64::from(transaction.type_id())),
187 transaction_hash: transaction.hash.into(),
188 index: U64::from(
189 transaction_index
190 .rpc_index
191 .unwrap_or(transaction_index.real_index),
195 ),
196 block_hash: transaction_index.block_hash.into(),
197 gas_used: (accumulated_gas_used - prior_gas_used).into(),
198 accumulated_gas_used: if include_accumulated_gas_used {
199 accumulated_gas_used.into()
200 } else {
201 None
202 },
203 gas_fee: gas_fee.into(),
204 burnt_gas_fee: receipt.burnt_gas_fee,
205 effective_gas_price,
206 from: RpcAddress::try_from_h160(transaction.sender, network)?,
207 to: match &action {
208 Action::Create => None,
209 Action::Call(address) => {
210 Some(RpcAddress::try_from_h160(address.clone(), network)?)
211 }
212 },
213 outcome_status: U64::from(outcome_status.in_space(space)),
214 contract_created: address,
215 logs: logs
216 .into_iter()
217 .filter(|l| {
218 if include_eth_receipt {
219 true
220 } else {
221 l.space == Space::Native
222 }
223 })
224 .map(|l| Log::try_from(l, network, include_eth_receipt))
225 .collect::<Result<_, _>>()?,
226 logs_bloom: log_bloom,
227 state_root: maybe_state_root
228 .map_or_else(Default::default, Into::into),
229 epoch_number: epoch_number.map(U64::from),
230 tx_exec_error_msg,
231 gas_covered_by_sponsor: gas_sponsor_paid,
232 storage_covered_by_sponsor: storage_sponsor_paid,
233 storage_collateralized,
234 storage_released: storage_released
235 .into_iter()
236 .map(|sc| StorageChange::try_from(sc, network))
237 .collect::<Result<_, _>>()?,
238 space: if include_eth_receipt {
239 Some(space)
240 } else {
241 None
242 },
243 })
244 }
245}