client/rpc/types/cfx/
receipt.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use 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    /// Transaction hash.
45    pub transaction_hash: H256,
46    /// Transaction index within the block.
47    pub index: U64,
48    /// Block hash.
49    pub block_hash: H256,
50    /// Epoch number where this transaction was in.
51    pub epoch_number: Option<U64>,
52    /// Address of the sender.
53    pub from: RpcAddress,
54    /// Address of the receiver, null when it's a contract creation
55    /// transaction.
56    pub to: Option<RpcAddress>,
57    /// The gas used in the execution of the transaction.
58    pub gas_used: U256,
59    /// The total gas used (not gas charged) in the block following execution
60    /// of the transaction.
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub accumulated_gas_used: Option<U256>,
63    /// The gas fee charged in the execution of the transaction.
64    pub gas_fee: U256,
65    pub effective_gas_price: U256,
66    /// Address of contract created if the transaction action is create.
67    pub contract_created: Option<RpcAddress>,
68    /// Array of log objects, which this transaction generated.
69    pub logs: Vec<Log>,
70    /// Bloom filter for light clients to quickly retrieve related logs.
71    pub logs_bloom: Bloom,
72    /// State root.
73    pub state_root: H256,
74    /// Transaction outcome.
75    pub outcome_status: U64,
76    /// Detailed error message if tx execution is unsuccessful. Error message
77    /// is None if tx execution is successful or it can not be offered.
78    /// Error message can not be offered by light client.
79    pub tx_exec_error_msg: Option<String>,
80    // Whether gas costs were covered by the sponsor.
81    pub gas_covered_by_sponsor: bool,
82    // Whether storage costs were covered by the sponsor.
83    pub storage_covered_by_sponsor: bool,
84    // The amount of storage collateralized by the sender.
85    pub storage_collateralized: U64,
86    // Storage collaterals released during the execution of the transaction.
87    pub storage_released: Vec<StorageChange>,
88    /// Transaction space.
89    #[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        // this is an array, but it will only have at most one element:
167        // the storage collateral of the sender address.
168        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                    // FIXME(thegaram): this is triggered on light nodes, and
192                    // maybe in some other cases as well.
193                    // is there a better way to handle this?
194                    .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}