primitives/
block.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::{BlockHeader, SignedTransaction, TransactionWithSignature};
6use byteorder::{ByteOrder, LittleEndian};
7use cfx_types::{Space, H256, U256};
8use keccak_hash::keccak;
9use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
10use rand::Rng;
11use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
12use siphasher::sip::SipHasher24;
13use std::{
14    fmt::{Debug, Formatter},
15    hash::Hasher,
16    sync::Arc,
17};
18
19pub type BlockNumber = u64;
20pub type BlockHeight = u64;
21
22/// A block, encoded as it is on the block chain.
23#[derive(Debug, Clone, PartialEq)]
24pub struct Block {
25    /// The header hash of this block.
26    pub block_header: BlockHeader,
27    /// The transactions in this block.
28    pub transactions: Vec<Arc<SignedTransaction>>,
29    /// Approximated rlp size of the block.
30    pub approximated_rlp_size: usize,
31    /// Approximated rlp size of block with transaction public key.
32    pub approximated_rlp_size_with_public: usize,
33}
34
35impl MallocSizeOf for Block {
36    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
37        self.block_header.size_of(ops) + self.transactions.size_of(ops)
38    }
39}
40
41impl Block {
42    pub fn new(
43        block_header: BlockHeader, transactions: Vec<Arc<SignedTransaction>>,
44    ) -> Self {
45        Self::new_with_rlp_size(block_header, transactions, None, None)
46    }
47
48    pub fn new_with_rlp_size(
49        block_header: BlockHeader, transactions: Vec<Arc<SignedTransaction>>,
50        rlp_size: Option<usize>, rlp_size_with_public: Option<usize>,
51    ) -> Self {
52        let approximated_rlp_size = match rlp_size {
53            Some(size) => size,
54            None => transactions
55                .iter()
56                .fold(block_header.approximated_rlp_size(), |accum, tx| {
57                    accum + tx.rlp_size()
58                }),
59        };
60
61        let approximated_rlp_size_with_public = match rlp_size_with_public {
62            Some(size) => size,
63            None => approximated_rlp_size + transactions.len() * 84, /* Sender(20B) + Public(64B) */
64        };
65
66        Block {
67            block_header,
68            transactions,
69            approximated_rlp_size,
70            approximated_rlp_size_with_public,
71        }
72    }
73
74    pub fn hash(&self) -> H256 { self.block_header.hash() }
75
76    /// Approximated rlp size of the block.
77    pub fn approximated_rlp_size(&self) -> usize { self.approximated_rlp_size }
78
79    /// Approximated rlp size of block with transaction public key.
80    pub fn approximated_rlp_size_with_public(&self) -> usize {
81        self.approximated_rlp_size_with_public
82    }
83
84    pub fn total_gas(&self) -> U256 {
85        let mut sum = U256::from(0);
86        for t in &self.transactions {
87            sum += *t.gas();
88        }
89        sum
90    }
91
92    /// The size filled in the RPC response. It returns the approximate rlp size
93    /// of the block.
94    pub fn size(&self) -> usize {
95        // FIXME: Because the approximate rlp size of the header may deviate
96        // FIXME: from the real rlp, now we always recalculate this to
97        // FIXME: avoid it failing the test case. One possible long term
98        // FIXME: correct solution is to implement a size calculation
99        // FIXME: that is consistent with the rlp.
100        self.transactions
101            .iter()
102            .fold(0, |accum, tx| accum + tx.rlp_size())
103    }
104
105    pub fn transaction_hashes(&self, space_filter: Option<Space>) -> Vec<H256> {
106        if let Some(space) = space_filter {
107            self.transactions
108                .iter()
109                .filter(|tx| tx.space() == space)
110                .map(|tx| tx.hash())
111                .collect()
112        } else {
113            self.transactions.iter().map(|tx| tx.hash()).collect()
114        }
115    }
116
117    /// Construct a new compact block with random nonce
118    /// This block will be relayed with the new compact block to prevent
119    /// adversaries to make tx shortId collision
120    pub fn to_compact(&self) -> CompactBlock {
121        let nonce: u64 = rand::rng().random();
122        let (k0, k1) =
123            CompactBlock::get_shortid_key(&self.block_header, &nonce);
124        CompactBlock {
125            block_header: self.block_header.clone(),
126            nonce,
127            tx_short_ids: CompactBlock::create_shortids(
128                &self.transactions,
129                k0,
130                k1,
131            ),
132            // reconstructed_txns constructed here will not be used
133            reconstructed_txns: Vec::new(),
134        }
135    }
136
137    pub fn encode_body_with_tx_public(&self) -> Vec<u8> {
138        let mut stream = RlpStream::new();
139        stream.begin_list(self.transactions.len());
140        for tx in &self.transactions {
141            stream.append(tx.as_ref());
142        }
143        stream.drain()
144    }
145
146    pub fn decode_body_with_tx_public(
147        rlp: &Rlp,
148    ) -> Result<Vec<Arc<SignedTransaction>>, DecoderError> {
149        if rlp.as_raw().len() != rlp.payload_info()?.total() {
150            return Err(DecoderError::RlpIsTooBig);
151        }
152
153        let signed_transactions = rlp.as_list()?;
154        let mut transactions = Vec::with_capacity(signed_transactions.len());
155        for tx in signed_transactions {
156            transactions.push(Arc::new(tx));
157        }
158
159        Ok(transactions)
160    }
161
162    pub fn encode_with_tx_public(&self) -> Vec<u8> {
163        let mut stream = RlpStream::new();
164        stream
165            .begin_list(2)
166            .append(&self.block_header)
167            .append_raw(&*self.encode_body_with_tx_public(), 1);
168        stream.drain()
169    }
170
171    pub fn decode_with_tx_public(rlp: &Rlp) -> Result<Self, DecoderError> {
172        if rlp.as_raw().len() != rlp.payload_info()?.total() {
173            return Err(DecoderError::RlpIsTooBig);
174        }
175        if rlp.item_count()? != 2 {
176            return Err(DecoderError::RlpIncorrectListLen);
177        }
178
179        Ok(Block::new_with_rlp_size(
180            rlp.val_at(0)?,
181            Self::decode_body_with_tx_public(&rlp.at(1)?)?,
182            None,
183            Some(rlp.as_raw().len()),
184        ))
185    }
186}
187
188// The encode of Block only serializes TransactionWithSignature
189// without "sender" and "public" fields.
190impl Encodable for Block {
191    fn rlp_append(&self, stream: &mut RlpStream) {
192        stream.begin_list(2).append(&self.block_header);
193        stream.begin_list(self.transactions.len());
194        for tx in &self.transactions {
195            stream.append(&tx.transaction);
196        }
197    }
198}
199
200// The decode of Block only deserializes TransactionWithSignature
201// without "sender" and "public" fields. So need to recover public later.
202impl Decodable for Block {
203    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
204        if rlp.as_raw().len() != rlp.payload_info()?.total() {
205            return Err(DecoderError::RlpIsTooBig);
206        }
207        if rlp.item_count()? != 2 {
208            return Err(DecoderError::RlpIncorrectListLen);
209        }
210
211        let transactions = rlp.list_at::<TransactionWithSignature>(1)?;
212
213        let mut signed_transactions = Vec::with_capacity(transactions.len());
214        for tx in transactions {
215            let signed = SignedTransaction::new_unsigned(tx);
216            signed_transactions.push(Arc::new(signed));
217        }
218
219        Ok(Block::new_with_rlp_size(
220            rlp.val_at(0)?,
221            signed_transactions,
222            Some(rlp.as_raw().len()),
223            None,
224        ))
225    }
226}
227
228// TODO Some optimization may be made if short_id hash collission is detected,
229// but should be rare
230#[derive(Clone, PartialEq)]
231pub struct CompactBlock {
232    /// The block header
233    pub block_header: BlockHeader,
234    /// The nonce for use in short id calculation
235    pub nonce: u64,
236    /// A list of tx short ids
237    pub tx_short_ids: Vec<u8>,
238    /// Store the txns reconstructed, None means not received
239    pub reconstructed_txns: Vec<Option<Arc<SignedTransaction>>>,
240}
241
242impl Debug for CompactBlock {
243    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
244        write!(
245            f,
246            "CompactBlock{{ block_header: {:?}, nonce: {:?}}}",
247            self.block_header, self.nonce
248        )
249    }
250}
251
252impl MallocSizeOf for CompactBlock {
253    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
254        self.tx_short_ids.size_of(ops) + self.reconstructed_txns.size_of(ops)
255    }
256}
257
258impl CompactBlock {
259    const SHORT_ID_SIZE_IN_BYTES: usize = 6;
260
261    pub fn len(&self) -> usize {
262        self.tx_short_ids.len() / CompactBlock::SHORT_ID_SIZE_IN_BYTES
263    }
264
265    pub fn hash(&self) -> H256 { self.block_header.hash() }
266
267    pub fn get_shortid_key(header: &BlockHeader, nonce: &u64) -> (u64, u64) {
268        let mut stream = RlpStream::new();
269        stream.begin_list(2).append(header).append(nonce);
270        let to_hash = stream.out();
271        let key_hash: [u8; 32] = keccak(to_hash).into();
272        let k0 = LittleEndian::read_u64(&key_hash[0..8]);
273        let k1 = LittleEndian::read_u64(&key_hash[8..16]);
274        (k0, k1)
275    }
276
277    /// Compute Tx ShortId from hash
278    pub fn create_shortids(
279        transactions: &Vec<Arc<SignedTransaction>>, k0: u64, k1: u64,
280    ) -> Vec<u8> {
281        let mut short_ids: Vec<u8> = vec![];
282
283        for tx in transactions {
284            let hash = tx.hash();
285            let random = CompactBlock::get_random_bytes(&hash, k0, k1);
286
287            short_ids.push(((random & 0xff00) >> 8) as u8);
288            short_ids.push((random & 0xff) as u8);
289            short_ids.push(hash[28]);
290            short_ids.push(hash[29]);
291            short_ids.push(hash[30]);
292            short_ids.push(hash[31]);
293        }
294        short_ids
295    }
296
297    pub fn to_u16(v1: u8, v2: u8) -> u16 { ((v1 as u16) << 8) + v2 as u16 }
298
299    pub fn to_u32(v1: u8, v2: u8, v3: u8, v4: u8) -> u32 {
300        ((v1 as u32) << 24)
301            + ((v2 as u32) << 16)
302            + ((v3 as u32) << 8)
303            + v4 as u32
304    }
305
306    pub fn get_random_bytes(
307        transaction_id: &H256, key1: u64, key2: u64,
308    ) -> u16 {
309        let mut hasher = SipHasher24::new_with_keys(key1, key2);
310        hasher.write(transaction_id.as_ref());
311        (hasher.finish() & 0xffff) as u16
312    }
313
314    pub fn get_decomposed_short_ids(&self) -> (Vec<u16>, Vec<u32>) {
315        let mut random_bytes_vector: Vec<u16> = Vec::new();
316        let mut fixed_bytes_vector: Vec<u32> = Vec::new();
317
318        for i in (0..self.tx_short_ids.len())
319            .step_by(CompactBlock::SHORT_ID_SIZE_IN_BYTES)
320        {
321            random_bytes_vector.push(CompactBlock::to_u16(
322                self.tx_short_ids[i],
323                self.tx_short_ids[i + 1],
324            ));
325            fixed_bytes_vector.push(CompactBlock::to_u32(
326                self.tx_short_ids[i + 2],
327                self.tx_short_ids[i + 3],
328                self.tx_short_ids[i + 4],
329                self.tx_short_ids[i + 5],
330            ));
331        }
332
333        (random_bytes_vector, fixed_bytes_vector)
334    }
335}
336
337impl Encodable for CompactBlock {
338    fn rlp_append(&self, steam: &mut RlpStream) {
339        steam
340            .begin_list(3)
341            .append(&self.block_header)
342            .append(&self.nonce)
343            .append(&self.tx_short_ids);
344    }
345}
346
347impl Decodable for CompactBlock {
348    fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
349        let short_ids: Vec<u8> = rlp.val_at(2)?;
350        if short_ids.len() % CompactBlock::SHORT_ID_SIZE_IN_BYTES != 0 {
351            return Err(DecoderError::Custom("Compact Block length Error!"));
352        }
353        Ok(CompactBlock {
354            block_header: rlp.val_at(0)?,
355            nonce: rlp.val_at(1)?,
356            tx_short_ids: short_ids,
357            reconstructed_txns: Vec::new(),
358        })
359    }
360}