1use 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
33const SHA3_HASH_OF_EMPTY: &str =
35 "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";
36
37#[derive(Debug, Clone)]
39pub enum BlockTransactions {
40 Hashes(Vec<H256>),
42 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#[derive(Debug, Serialize, Clone)]
60#[serde(rename_all = "camelCase")]
61pub struct Block<H = Header> {
62 #[serde(flatten)]
64 pub header: H,
65 pub transactions: BlockTransactions,
67 pub uncles: Vec<H256>,
69}
70
71#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
73#[serde(rename_all = "camelCase")]
74pub struct Header {
75 pub hash: H256,
77 pub parent_hash: H256,
79 #[serde(rename = "sha3Uncles")]
81 pub uncles_hash: H256,
82 pub author: H160,
84 pub miner: H160,
86 pub state_root: H256,
88 pub transactions_root: H256,
90 pub receipts_root: H256,
92 pub number: U256,
94 pub gas_used: U256,
96 pub gas_limit: U256,
98 pub espace_gas_limit: U256,
101 pub extra_data: Bytes,
103 pub logs_bloom: H2048,
105 pub timestamp: U256,
107 pub difficulty: U256,
109 pub total_difficulty: U256,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub base_fee_per_gas: Option<U256>,
114 pub size: U256,
116 pub nonce: H64,
118 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()), Some(pb.pivot_header.height().into()), Some(idx.into()), ),
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 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 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 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 }
259
260#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
262#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
263pub struct BlockOverrides {
264 #[serde(
270 default,
271 skip_serializing_if = "Option::is_none",
272 alias = "blockNumber"
273 )]
274 pub number: Option<U256>,
275 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub difficulty: Option<U256>,
278 #[serde(
281 default,
282 skip_serializing_if = "Option::is_none",
283 alias = "timestamp",
284 )]
286 pub time: Option<u64>,
287 #[serde(
289 default,
290 skip_serializing_if = "Option::is_none",
291 )
293 ]
294 pub gas_limit: Option<u64>,
295 #[serde(
297 default,
298 skip_serializing_if = "Option::is_none",
299 alias = "feeRecipient"
300 )]
301 pub coinbase: Option<Address>,
302 #[serde(
304 default,
305 skip_serializing_if = "Option::is_none",
306 alias = "prevRandao"
307 )]
308 pub random: Option<H256>,
309 #[serde(
311 default,
312 skip_serializing_if = "Option::is_none",
313 alias = "baseFeePerGas"
314 )]
315 pub base_fee: Option<U256>,
316 #[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}