cfx_rpc_eth_types/
block.rs

1// Copyright 2021 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
5// Copyright 2015-2020 Parity Technologies (UK) Ltd.
6// This file is part of OpenEthereum.
7
8// OpenEthereum is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// OpenEthereum is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17
18// You should have received a copy of the GNU General Public License
19// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.
20
21use crate::{Bytes, Transaction};
22use cfx_rpc_cfx_types::PhantomBlock;
23use cfx_types::{
24    hexstr_to_h256, Address, Bloom as H2048, Space, H160, H256, H64, U256,
25};
26use primitives::receipt::EVM_SPACE_SUCCESS;
27use serde::{Deserialize, Serialize, Serializer};
28use std::collections::BTreeMap;
29
30const SHA3_HASH_OF_EMPTY_UNCLE: &str =
31    "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
32
33// sha3 hash of empty tx, state, receipt
34const SHA3_HASH_OF_EMPTY: &str =
35    "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";
36
37/// Block Transactions
38#[derive(Debug, Clone)]
39pub enum BlockTransactions {
40    /// Only hashes
41    Hashes(Vec<H256>),
42    /// Full transactions
43    Full(Vec<Transaction>),
44}
45
46impl Serialize for BlockTransactions {
47    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
48    where S: Serializer {
49        match *self {
50            BlockTransactions::Hashes(ref hashes) => {
51                hashes.serialize(serializer)
52            }
53            BlockTransactions::Full(ref ts) => ts.serialize(serializer),
54        }
55    }
56}
57
58/// Block representation
59#[derive(Debug, Serialize, Clone)]
60#[serde(rename_all = "camelCase")]
61pub struct Block<H = Header> {
62    /// Header of the block.
63    #[serde(flatten)]
64    pub header: H,
65    /// Transactions
66    pub transactions: BlockTransactions,
67    /// Uncles' hashes
68    pub uncles: Vec<H256>,
69}
70
71/// Block header representation.
72#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
73#[serde(rename_all = "camelCase")]
74pub struct Header {
75    /// Hash of the block
76    pub hash: H256,
77    /// Hash of the parent
78    pub parent_hash: H256,
79    /// Hash of the uncles
80    #[serde(rename = "sha3Uncles")]
81    pub uncles_hash: H256,
82    /// Authors address
83    pub author: H160,
84    /// Alias of `author`
85    pub miner: H160,
86    /// State root hash
87    pub state_root: H256,
88    /// Transactions root hash
89    pub transactions_root: H256,
90    /// Transactions receipts root hash
91    pub receipts_root: H256,
92    /// Block number
93    pub number: U256,
94    /// Gas Used
95    pub gas_used: U256,
96    /// Gas Limit
97    pub gas_limit: U256,
98    /// Conflux espace gas limit, this is the real gas limit of the block
99    /// This is a conflux espace custom field
100    pub espace_gas_limit: U256,
101    /// Extra data
102    pub extra_data: Bytes,
103    /// Logs bloom
104    pub logs_bloom: H2048,
105    /// Timestamp
106    pub timestamp: U256,
107    /// Difficulty
108    pub difficulty: U256,
109    /// Total difficulty
110    pub total_difficulty: U256,
111    /// Base fee
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub base_fee_per_gas: Option<U256>,
114    /// Size in bytes
115    pub size: U256,
116    /// Nonce
117    pub nonce: H64,
118    /// Mix hash
119    pub mix_hash: H256,
120}
121
122impl Block {
123    pub fn from_phantom(pb: &PhantomBlock, full: bool) -> Self {
124        let transactions = if full {
125            BlockTransactions::Full(
126                pb.transactions
127                    .iter()
128                    .enumerate()
129                    .map(|(idx, t)| {
130                        let status = pb.receipts[idx]
131                            .outcome_status
132                            .in_space(Space::Ethereum);
133
134                        let contract_address =
135                            match Transaction::deployed_contract_address(&**t) {
136                                Some(a) if status == EVM_SPACE_SUCCESS => {
137                                    Some(a)
138                                }
139                                _ => None,
140                            };
141
142                        Transaction::from_signed(
143                            &**t,
144                            (
145                                Some(pb.pivot_header.hash()),          // block_hash
146                                Some(pb.pivot_header.height().into()), // block_number
147                                Some(idx.into()), // transaction_index
148                            ),
149                            (Some(status.into()), contract_address),
150                        )
151                    })
152                    .collect(),
153            )
154        } else {
155            BlockTransactions::Hashes(
156                pb.transactions.iter().map(|t| t.hash()).collect(),
157            )
158        };
159
160        let header = Header::from_phantom(pb);
161
162        Block {
163            header,
164            uncles: vec![],
165            transactions,
166        }
167    }
168}
169
170impl Header {
171    pub fn from_phantom(pb: &PhantomBlock) -> Self {
172        // If there are no transactions, we use the empty hash for txRoot and
173        // receiptRoot. Another way to calculate transactions_root and
174        // receipts_root from transactions.
175        let (transactions_root, receipts_root) = if pb.transactions.len() > 0 {
176            (
177                pb.pivot_header.transactions_root().clone(),
178                pb.pivot_header.deferred_receipts_root().clone(),
179            )
180        } else {
181            (
182                hexstr_to_h256(SHA3_HASH_OF_EMPTY),
183                hexstr_to_h256(SHA3_HASH_OF_EMPTY),
184            )
185        };
186        Header {
187            hash: pb.pivot_header.hash(),
188            parent_hash: pb.pivot_header.parent_hash().clone(),
189            uncles_hash: hexstr_to_h256(SHA3_HASH_OF_EMPTY_UNCLE),
190            author: pb.pivot_header.author().clone(),
191            miner: pb.pivot_header.author().clone(),
192            state_root: pb.pivot_header.deferred_state_root().clone(),
193            transactions_root,
194            receipts_root,
195            // We use height to replace block number for ETH interface.
196            // Note: this will correspond to the epoch number.
197            number: pb.pivot_header.height().into(),
198            gas_used: pb
199                .receipts
200                .last()
201                .map(|r| r.accumulated_gas_used)
202                .unwrap_or_default(),
203            gas_limit: pb.pivot_header.espace_gas_limit(true).into(),
204            espace_gas_limit: pb.total_gas_limit,
205            extra_data: Default::default(),
206            logs_bloom: pb.bloom,
207            timestamp: pb.pivot_header.timestamp().into(),
208            difficulty: pb.pivot_header.difficulty().into(),
209            total_difficulty: 0.into(),
210            base_fee_per_gas: pb
211                .pivot_header
212                .base_price()
213                .map(|x| x[Space::Ethereum]),
214            // Note: we allow U256 nonce in Stratum and in the block.
215            // However, most mining clients use U64. Here we truncate
216            // to U64 to maintain compatibility with eth.
217            nonce: pb.pivot_header.nonce().low_u64().to_be_bytes().into(),
218            mix_hash: H256::default(),
219            size: pb
220                .transactions
221                .iter()
222                .fold(0, |acc, tx| acc + tx.rlp_size())
223                .into(),
224        }
225    }
226
227    //     pub fn new(h: &EthHeader, eip1559_transition: BlockNumber) -> Self {
228    //         let eip1559_enabled = h.number() >= eip1559_transition;
229    //         Header {
230    //             hash: Some(h.hash()),
231    // 			size: Some(h.rlp().as_raw().len().into()),
232    // 			parent_hash: h.parent_hash(),
233    // 			uncles_hash: h.uncles_hash(),
234    // 			author: h.author(),
235    // 			miner: h.author(),
236    // 			state_root: h.state_root(),
237    // 			transactions_root: h.transactions_root(),
238    // 			receipts_root: h.receipts_root(),
239    // 			number: Some(h.number().into()),
240    // 			gas_used: h.gas_used(),
241    // 			gas_limit: h.gas_limit(),
242    // 			logs_bloom: h.log_bloom(),
243    // 			timestamp: h.timestamp().into(),
244    // 			difficulty: h.difficulty(),
245    // 			extra_data: h.extra_data().into(),
246    // 			seal_fields: h.view().decode_seal(eip1559_enabled)
247    // 				.expect("Client/Miner returns only valid headers. We only serialize
248    // headers from Client/Miner; qed")
249    // .into_iter().map(Into::into).collect(), 			base_fee_per_gas: {
250    // 				if eip1559_enabled {
251    // 					Some(h.base_fee())
252    // 				} else {
253    // 					None
254    // 				}
255    // 			},
256    // 		}
257    //     }
258}
259
260/// BlockOverrides is a set of header fields to override.
261#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
262#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
263pub struct BlockOverrides {
264    /// Overrides the block number.
265    ///
266    /// For `eth_callMany` this will be the block number of the first simulated
267    /// block. Each following block increments its block number by 1
268    // Note: geth uses `number`, erigon uses `blockNumber`
269    #[serde(
270        default,
271        skip_serializing_if = "Option::is_none",
272        alias = "blockNumber"
273    )]
274    pub number: Option<U256>,
275    /// Overrides the difficulty of the block.
276    #[serde(default, skip_serializing_if = "Option::is_none")]
277    pub difficulty: Option<U256>,
278    /// Overrides the timestamp of the block.
279    // Note: geth uses `time`, erigon uses `timestamp`
280    #[serde(
281            default,
282            skip_serializing_if = "Option::is_none",
283            alias = "timestamp",
284            // with = "alloy_serde::quantity::opt"
285        )]
286    pub time: Option<u64>,
287    /// Overrides the gas limit of the block.
288    #[serde(
289            default,
290            skip_serializing_if = "Option::is_none",
291            // with = "alloy_serde::quantity::opt"
292        )
293    ]
294    pub gas_limit: Option<u64>,
295    /// Overrides the coinbase address of the block.
296    #[serde(
297        default,
298        skip_serializing_if = "Option::is_none",
299        alias = "feeRecipient"
300    )]
301    pub coinbase: Option<Address>,
302    /// Overrides the prevrandao of the block.
303    #[serde(
304        default,
305        skip_serializing_if = "Option::is_none",
306        alias = "prevRandao"
307    )]
308    pub random: Option<H256>,
309    /// Overrides the basefee of the block.
310    #[serde(
311        default,
312        skip_serializing_if = "Option::is_none",
313        alias = "baseFeePerGas"
314    )]
315    pub base_fee: Option<U256>,
316    /// A dictionary that maps blockNumber to a user-defined hash. It can be
317    /// queried from the EVM opcode BLOCKHASH.
318    #[serde(default, skip_serializing_if = "Option::is_none")]
319    pub block_hash: Option<BTreeMap<u64, H256>>,
320}
321
322#[cfg(test)]
323mod tests {
324    use super::{Block, BlockTransactions, Header};
325    use crate::Bytes;
326    use cfx_types::{Bloom as H2048, H160, H256, H64, U256};
327
328    #[test]
329    fn test_serialize_block() {
330        let block = Block {
331            header: Header {
332                hash: H256::default(),
333                parent_hash: H256::default(),
334                uncles_hash: H256::default(),
335                author: H160::default(),
336                miner: H160::default(),
337                state_root: H256::default(),
338                transactions_root: H256::default(),
339                receipts_root: H256::default(),
340                number: U256::default(),
341                gas_used: U256::default(),
342                gas_limit: U256::default(),
343                espace_gas_limit: U256::default(),
344                extra_data: Bytes::default(),
345                logs_bloom: H2048::default(),
346                timestamp: U256::default(),
347                difficulty: U256::default(),
348                total_difficulty: 0.into(),
349                base_fee_per_gas: None,
350                size: 69.into(),
351                nonce: H64::default(),
352                mix_hash: H256::default(),
353            },
354            uncles: vec![],
355            transactions: BlockTransactions::Hashes(vec![].into()),
356        };
357        let serialized_block = serde_json::to_string(&block).unwrap();
358
359        assert_eq!(
360            serialized_block,
361            r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","espaceGasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","size":"0x45","nonce":"0x0000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactions":[],"uncles":[]}"#
362        );
363    }
364}