1use crate::{Log, RpcAddress};
6use cfx_addr::Network;
7use cfx_types::{
8 address_util::AddressUtil, cal_contract_address, Bloom,
9 CreateContractAddressType, Space, SpaceMap, H256, U256, U64,
10};
11use cfx_util_macros::bail;
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>, maybe_base_price: Option<SpaceMap<U256>>,
100 maybe_state_root: Option<H256>, tx_exec_error_msg: Option<String>,
101 network: Network, include_eth_receipt: bool,
102 include_accumulated_gas_used: bool,
103 ) -> Result<Receipt, String> {
104 let PrimitiveReceipt {
105 accumulated_gas_used,
106 gas_fee,
107 gas_sponsor_paid,
108 log_bloom,
109 logs,
110 outcome_status,
111 storage_collateralized,
112 storage_released,
113 storage_sponsor_paid,
114 ..
115 } = receipt;
116
117 let (address, action, space) = match transaction.unsigned {
118 Transaction::Native(ref unsigned) => {
119 if Action::Create == unsigned.action()
120 && outcome_status == TransactionStatus::Success
121 {
122 let (mut created_address, _) = cal_contract_address(
123 CreateContractAddressType::FromSenderNonceAndCodeHash,
124 &transaction.sender,
125 unsigned.nonce(),
126 unsigned.data(),
127 );
128 created_address.set_contract_type_bits();
129 let address = Some(RpcAddress::try_from_h160(
130 created_address,
131 network,
132 )?);
133 (address, unsigned.action().clone(), Space::Native)
134 } else {
135 (None, unsigned.action().clone(), Space::Native)
136 }
137 }
138 Transaction::Ethereum(ref unsigned) => {
139 if include_eth_receipt {
140 if Action::Create == unsigned.action()
141 && outcome_status == TransactionStatus::Success
142 {
143 let (created_address, _) = cal_contract_address(
144 CreateContractAddressType::FromSenderNonce,
145 &transaction.sender,
146 unsigned.nonce(),
147 unsigned.data(),
148 );
149 let address = Some(RpcAddress::try_from_h160(
150 created_address,
151 network,
152 )?);
153 (address, unsigned.action().clone(), Space::Ethereum)
154 } else {
155 (None, unsigned.action().clone(), Space::Ethereum)
156 }
157 } else {
158 bail!(format!("Does not support EIP-155 transaction in Conflux space RPC. get_receipt for tx: {:?}",transaction));
159 }
160 }
161 };
162
163 let storage_collateralized = storage_collateralized
166 .get(0)
167 .map(|sc| sc.collaterals)
168 .map(Into::into)
169 .unwrap_or_default();
170
171 let effective_gas_price = if let Some(base_price) = maybe_base_price {
172 let base_price = base_price[transaction.space()];
173 if *transaction.gas_price() < base_price {
174 *transaction.gas_price()
175 } else {
176 transaction.effective_gas_price(&base_price)
177 }
178 } else {
179 *transaction.gas_price()
180 };
181
182 Ok(Receipt {
183 transaction_type: Some(U64::from(transaction.type_id())),
184 transaction_hash: transaction.hash.into(),
185 index: U64::from(
186 transaction_index
187 .rpc_index
188 .unwrap_or(transaction_index.real_index),
192 ),
193 block_hash: transaction_index.block_hash.into(),
194 gas_used: (accumulated_gas_used - prior_gas_used).into(),
195 accumulated_gas_used: if include_accumulated_gas_used {
196 accumulated_gas_used.into()
197 } else {
198 None
199 },
200 gas_fee: gas_fee.into(),
201 burnt_gas_fee: receipt.burnt_gas_fee,
202 effective_gas_price,
203 from: RpcAddress::try_from_h160(transaction.sender, network)?,
204 to: match &action {
205 Action::Create => None,
206 Action::Call(address) => {
207 Some(RpcAddress::try_from_h160(address.clone(), network)?)
208 }
209 },
210 outcome_status: U64::from(outcome_status.in_space(space)),
211 contract_created: address,
212 logs: logs
213 .into_iter()
214 .filter(|l| {
215 if include_eth_receipt {
216 true
217 } else {
218 l.space == Space::Native
219 }
220 })
221 .map(|l| Log::try_from(l, network, include_eth_receipt))
222 .collect::<Result<_, _>>()?,
223 logs_bloom: log_bloom,
224 state_root: maybe_state_root
225 .map_or_else(Default::default, Into::into),
226 epoch_number: epoch_number.map(U64::from),
227 tx_exec_error_msg,
228 gas_covered_by_sponsor: gas_sponsor_paid,
229 storage_covered_by_sponsor: storage_sponsor_paid,
230 storage_collateralized,
231 storage_released: storage_released
232 .into_iter()
233 .map(|sc| StorageChange::try_from(sc, network))
234 .collect::<Result<_, _>>()?,
235 space: if include_eth_receipt {
236 Some(space)
237 } else {
238 None
239 },
240 })
241 }
242}