mod base_price;
pub use base_price::{
compute_next_price, compute_next_price_tuple, estimate_gas_used_boundary,
estimate_max_possible_gas,
};
use crate::{
block::BlockHeight, bytes::Bytes, hash::keccak, pos::PosBlockId,
receipt::BlockReceipts, MERKLE_NULL_NODE, NULL_EPOCH,
};
use cfx_parameters::block::{cspace_block_gas_limit, espace_block_gas_limit};
use cfx_types::{
Address, Bloom, Space, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256,
};
use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps};
use once_cell::sync::OnceCell;
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use rlp_derive::{RlpDecodable, RlpEncodable};
use std::{
mem,
ops::{Deref, DerefMut},
sync::Arc,
};
const HEADER_LIST_MIN_LEN: usize = 13;
pub static CIP112_TRANSITION_HEIGHT: OnceCell<u64> = OnceCell::new();
pub const BASE_PRICE_CHANGE_DENOMINATOR: usize = 8;
#[derive(Clone, Debug, Eq)]
pub struct BlockHeaderRlpPart {
parent_hash: H256,
height: BlockHeight,
timestamp: u64,
author: Address,
transactions_root: H256,
deferred_state_root: H256,
deferred_receipts_root: H256,
deferred_logs_bloom_hash: H256,
blame: u32,
difficulty: U256,
adaptive: bool,
gas_limit: U256,
referee_hashes: Vec<H256>,
custom: Vec<Bytes>,
nonce: U256,
pos_reference: Option<H256>,
base_price: Option<BasePrice>,
}
impl PartialEq for BlockHeaderRlpPart {
fn eq(&self, o: &BlockHeaderRlpPart) -> bool {
self.parent_hash == o.parent_hash
&& self.height == o.height
&& self.timestamp == o.timestamp
&& self.author == o.author
&& self.transactions_root == o.transactions_root
&& self.deferred_state_root == o.deferred_state_root
&& self.deferred_receipts_root == o.deferred_receipts_root
&& self.deferred_logs_bloom_hash == o.deferred_logs_bloom_hash
&& self.blame == o.blame
&& self.difficulty == o.difficulty
&& self.adaptive == o.adaptive
&& self.gas_limit == o.gas_limit
&& self.referee_hashes == o.referee_hashes
&& self.custom == o.custom
&& self.pos_reference == o.pos_reference
&& self.base_price == o.base_price
}
}
#[derive(Clone, Debug, Eq)]
pub struct BlockHeader {
rlp_part: BlockHeaderRlpPart,
hash: Option<H256>,
pub pow_hash: Option<H256>,
pub approximated_rlp_size: usize,
}
impl Deref for BlockHeader {
type Target = BlockHeaderRlpPart;
fn deref(&self) -> &Self::Target { &self.rlp_part }
}
impl DerefMut for BlockHeader {
fn deref_mut(&mut self) -> &mut BlockHeaderRlpPart { &mut self.rlp_part }
}
impl MallocSizeOf for BlockHeader {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.referee_hashes.size_of(ops) + self.custom.size_of(ops)
}
}
impl PartialEq for BlockHeader {
fn eq(&self, o: &BlockHeader) -> bool { self.rlp_part == o.rlp_part }
}
impl BlockHeader {
pub fn approximated_rlp_size(&self) -> usize { self.approximated_rlp_size }
pub fn parent_hash(&self) -> &H256 { &self.parent_hash }
pub fn height(&self) -> u64 { self.height }
pub fn timestamp(&self) -> u64 { self.timestamp }
pub fn author(&self) -> &Address { &self.author }
pub fn transactions_root(&self) -> &H256 { &self.transactions_root }
pub fn deferred_state_root(&self) -> &H256 { &self.deferred_state_root }
pub fn deferred_receipts_root(&self) -> &H256 {
&self.deferred_receipts_root
}
pub fn deferred_logs_bloom_hash(&self) -> &H256 {
&self.deferred_logs_bloom_hash
}
pub fn blame(&self) -> u32 { self.blame }
pub fn difficulty(&self) -> &U256 { &self.difficulty }
pub fn adaptive(&self) -> bool { self.adaptive }
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }
pub fn core_space_gas_limit(&self) -> U256 {
cspace_block_gas_limit(
self.base_price.is_some(),
self.gas_limit().to_owned(),
)
}
pub fn espace_gas_limit(&self, can_pack: bool) -> U256 {
espace_block_gas_limit(can_pack, self.gas_limit().to_owned())
}
pub fn referee_hashes(&self) -> &Vec<H256> { &self.referee_hashes }
pub fn custom(&self) -> &Vec<Bytes> { &self.custom }
pub fn nonce(&self) -> U256 { self.nonce }
pub fn pos_reference(&self) -> &Option<PosBlockId> { &self.pos_reference }
pub fn base_price(&self) -> Option<SpaceMap<U256>> {
self.base_price.map(
|BasePrice {
core_base_price,
espace_base_price,
}| SpaceMap::new(core_base_price, espace_base_price),
)
}
pub fn space_base_price(&self, space: Space) -> Option<U256> {
self.base_price.map(|x| match space {
Space::Native => x.core_base_price,
Space::Ethereum => x.espace_base_price,
})
}
pub fn set_nonce(&mut self, nonce: U256) { self.nonce = nonce; }
pub fn set_timestamp(&mut self, timestamp: u64) {
self.timestamp = timestamp;
}
pub fn set_custom(&mut self, custom: Vec<Bytes>) { self.custom = custom; }
pub fn compute_hash(&mut self) -> H256 {
let hash = self.hash();
self.hash = Some(hash);
hash
}
pub fn hash(&self) -> H256 {
self.hash.unwrap_or_else(|| keccak(self.rlp()))
}
pub fn problem_hash(&self) -> H256 { keccak(self.rlp_without_nonce()) }
pub fn rlp_without_nonce(&self) -> Bytes {
let mut stream = RlpStream::new();
self.stream_rlp_without_nonce(&mut stream);
stream.out()
}
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new();
self.stream_rlp(&mut stream);
stream.out()
}
fn stream_rlp_without_nonce(&self, stream: &mut RlpStream) {
let adaptive_n = if self.adaptive { 1 as u8 } else { 0 as u8 };
let list_len = HEADER_LIST_MIN_LEN
+ self.pos_reference.is_some() as usize
+ self.base_price.is_some() as usize
+ self.custom.len();
stream
.begin_list(list_len)
.append(&self.parent_hash)
.append(&self.height)
.append(&self.timestamp)
.append(&self.author)
.append(&self.transactions_root)
.append(&self.deferred_state_root)
.append(&self.deferred_receipts_root)
.append(&self.deferred_logs_bloom_hash)
.append(&self.blame)
.append(&self.difficulty)
.append(&adaptive_n)
.append(&self.gas_limit)
.append_list(&self.referee_hashes);
if self.pos_reference.is_some() {
stream.append(&self.pos_reference);
}
if self.base_price.is_some() {
stream.append(&self.base_price);
}
for b in &self.custom {
if self.height
>= *CIP112_TRANSITION_HEIGHT.get().expect("initialized")
{
stream.append(b);
} else {
stream.append_raw(b, 1);
}
}
}
fn stream_rlp(&self, stream: &mut RlpStream) {
let adaptive_n = if self.adaptive { 1 as u8 } else { 0 as u8 };
let list_len = HEADER_LIST_MIN_LEN
+ 1
+ self.pos_reference.is_some() as usize
+ self.base_price.is_some() as usize
+ self.custom.len();
stream
.begin_list(list_len)
.append(&self.parent_hash)
.append(&self.height)
.append(&self.timestamp)
.append(&self.author)
.append(&self.transactions_root)
.append(&self.deferred_state_root)
.append(&self.deferred_receipts_root)
.append(&self.deferred_logs_bloom_hash)
.append(&self.blame)
.append(&self.difficulty)
.append(&adaptive_n)
.append(&self.gas_limit)
.append_list(&self.referee_hashes)
.append(&self.nonce);
if self.pos_reference.is_some() {
stream.append(&self.pos_reference);
}
if self.base_price.is_some() {
stream.append(&self.base_price);
}
for b in &self.custom {
if self.height
>= *CIP112_TRANSITION_HEIGHT.get().expect("initialized")
{
stream.append(b);
} else {
stream.append_raw(b, 1);
}
}
}
pub fn stream_rlp_with_pow_hash(&self, stream: &mut RlpStream) {
let adaptive_n = if self.adaptive { 1 as u8 } else { 0 as u8 };
let list_len = HEADER_LIST_MIN_LEN
+ 2
+ self.pos_reference.is_some() as usize
+ self.base_price.is_some() as usize
+ self.custom.len();
stream
.begin_list(list_len)
.append(&self.parent_hash)
.append(&self.height)
.append(&self.timestamp)
.append(&self.author)
.append(&self.transactions_root)
.append(&self.deferred_state_root)
.append(&self.deferred_receipts_root)
.append(&self.deferred_logs_bloom_hash)
.append(&self.blame)
.append(&self.difficulty)
.append(&adaptive_n)
.append(&self.gas_limit)
.append_list(&self.referee_hashes)
.append(&self.nonce)
.append(&self.pow_hash);
if self.pos_reference.is_some() {
stream.append(&self.pos_reference);
}
if self.base_price.is_some() {
stream.append(&self.base_price);
}
for b in &self.custom {
if self.height
>= *CIP112_TRANSITION_HEIGHT.get().expect("initialized")
{
stream.append(b);
} else {
stream.append_raw(b, 1);
}
}
}
pub fn decode_with_pow_hash(bytes: &[u8]) -> Result<Self, DecoderError> {
let r = Rlp::new(bytes);
let mut rlp_part = BlockHeaderRlpPart {
parent_hash: r.val_at(0)?,
height: r.val_at(1)?,
timestamp: r.val_at(2)?,
author: r.val_at(3)?,
transactions_root: r.val_at(4)?,
deferred_state_root: r.val_at(5)?,
deferred_receipts_root: r.val_at(6)?,
deferred_logs_bloom_hash: r.val_at(7)?,
blame: r.val_at(8)?,
difficulty: r.val_at(9)?,
adaptive: r.val_at::<u8>(10)? == 1,
gas_limit: r.val_at(11)?,
referee_hashes: r.list_at(12)?,
custom: vec![],
nonce: r.val_at(13)?,
pos_reference: r.val_at(15).unwrap_or(None),
base_price: r.val_at(16).unwrap_or(None),
};
let pow_hash = r.val_at(14)?;
for i in (15
+ rlp_part.pos_reference.is_some() as usize
+ rlp_part.base_price.is_some() as usize)
..r.item_count()?
{
if rlp_part.height
>= *CIP112_TRANSITION_HEIGHT.get().expect("initialized")
{
rlp_part.custom.push(r.val_at(i)?);
} else {
rlp_part.custom.push(r.at(i)?.as_raw().to_vec());
}
}
let mut header = BlockHeader {
rlp_part,
hash: None,
pow_hash,
approximated_rlp_size: bytes.len(),
};
header.compute_hash();
Ok(header)
}
pub fn size(&self) -> usize {
0
}
}
pub struct BlockHeaderBuilder {
parent_hash: H256,
height: u64,
timestamp: u64,
author: Address,
transactions_root: H256,
deferred_state_root: H256,
deferred_receipts_root: H256,
deferred_logs_bloom_hash: H256,
blame: u32,
difficulty: U256,
adaptive: bool,
gas_limit: U256,
referee_hashes: Vec<H256>,
custom: Vec<Bytes>,
nonce: U256,
pos_reference: Option<PosBlockId>,
base_price: Option<BasePrice>,
}
impl BlockHeaderBuilder {
pub fn new() -> Self {
Self {
parent_hash: NULL_EPOCH,
height: 0,
timestamp: 0,
author: Address::default(),
transactions_root: MERKLE_NULL_NODE,
deferred_state_root: Default::default(),
deferred_receipts_root: Default::default(),
deferred_logs_bloom_hash: KECCAK_EMPTY_BLOOM,
blame: 0,
difficulty: U256::default(),
adaptive: false,
gas_limit: U256::zero(),
referee_hashes: Vec::new(),
custom: Vec::new(),
nonce: U256::zero(),
pos_reference: None,
base_price: None,
}
}
pub fn with_parent_hash(&mut self, parent_hash: H256) -> &mut Self {
self.parent_hash = parent_hash;
self
}
pub fn with_height(&mut self, height: u64) -> &mut Self {
self.height = height;
self
}
pub fn with_timestamp(&mut self, timestamp: u64) -> &mut Self {
self.timestamp = timestamp;
self
}
pub fn with_author(&mut self, author: Address) -> &mut Self {
self.author = author;
self
}
pub fn with_transactions_root(
&mut self, transactions_root: H256,
) -> &mut Self {
self.transactions_root = transactions_root;
self
}
pub fn with_deferred_state_root(
&mut self, deferred_state_root: H256,
) -> &mut Self {
self.deferred_state_root = deferred_state_root;
self
}
pub fn with_deferred_receipts_root(
&mut self, deferred_receipts_root: H256,
) -> &mut Self {
self.deferred_receipts_root = deferred_receipts_root;
self
}
pub fn with_deferred_logs_bloom_hash(
&mut self, deferred_logs_bloom_hash: H256,
) -> &mut Self {
self.deferred_logs_bloom_hash = deferred_logs_bloom_hash;
self
}
pub fn with_blame(&mut self, blame: u32) -> &mut Self {
self.blame = blame;
self
}
pub fn with_difficulty(&mut self, difficulty: U256) -> &mut Self {
self.difficulty = difficulty;
self
}
pub fn with_adaptive(&mut self, adaptive: bool) -> &mut Self {
self.adaptive = adaptive;
self
}
pub fn with_gas_limit(&mut self, gas_limit: U256) -> &mut Self {
self.gas_limit = gas_limit;
self
}
pub fn with_referee_hashes(
&mut self, referee_hashes: Vec<H256>,
) -> &mut Self {
self.referee_hashes = referee_hashes;
self
}
pub fn with_custom(&mut self, custom: Vec<Bytes>) -> &mut Self {
self.custom = custom;
self
}
pub fn with_nonce(&mut self, nonce: U256) -> &mut Self {
self.nonce = nonce;
self
}
pub fn with_pos_reference(
&mut self, pos_reference: Option<PosBlockId>,
) -> &mut Self {
self.pos_reference = pos_reference;
self
}
pub fn with_base_price(
&mut self, maybe_base_price: Option<SpaceMap<U256>>,
) -> &mut Self {
self.base_price = maybe_base_price.map(|x| BasePrice {
core_base_price: x[Space::Native],
espace_base_price: x[Space::Ethereum],
});
self
}
pub fn build(&self) -> BlockHeader {
let mut block_header = BlockHeader {
rlp_part: BlockHeaderRlpPart {
parent_hash: self.parent_hash,
height: self.height,
timestamp: self.timestamp,
author: self.author,
transactions_root: self.transactions_root,
deferred_state_root: self.deferred_state_root,
deferred_receipts_root: self.deferred_receipts_root,
deferred_logs_bloom_hash: self.deferred_logs_bloom_hash,
blame: self.blame,
difficulty: self.difficulty,
adaptive: self.adaptive,
gas_limit: self.gas_limit,
referee_hashes: self.referee_hashes.clone(),
custom: self.custom.clone(),
nonce: self.nonce,
pos_reference: self.pos_reference,
base_price: self.base_price.clone(),
},
hash: None,
pow_hash: None,
approximated_rlp_size: 0,
};
block_header.approximated_rlp_size =
mem::size_of::<BlockHeaderRlpPart>()
+ block_header
.referee_hashes
.size_of(&mut new_malloc_size_ops());
block_header
}
pub fn compute_block_logs_bloom_hash(
receipts: &Vec<Arc<BlockReceipts>>,
) -> H256 {
let bloom = receipts.iter().map(|x| &x.receipts).flatten().fold(
Bloom::zero(),
|mut b, r| {
b.accrue_bloom(&r.log_bloom);
b
},
);
keccak(bloom)
}
pub fn compute_aggregated_bloom(blooms: Vec<Bloom>) -> Bloom {
blooms.into_iter().fold(Bloom::zero(), |mut res, bloom| {
res.accrue_bloom(&bloom);
res
})
}
pub fn compute_blame_state_root_vec_root(roots: Vec<H256>) -> H256 {
let mut accumulated_root = roots.last().unwrap().clone();
for i in (0..(roots.len() - 1)).rev() {
accumulated_root =
BlockHeaderBuilder::compute_blame_state_root_incremental(
roots[i],
accumulated_root,
);
}
accumulated_root
}
pub fn compute_blame_state_root_incremental(
first_root: H256, remaining_root: H256,
) -> H256 {
let mut buffer = Vec::with_capacity(H256::len_bytes() * 2);
buffer.extend_from_slice(first_root.as_bytes());
buffer.extend_from_slice(remaining_root.as_bytes());
keccak(&buffer)
}
}
impl Encodable for BlockHeader {
fn rlp_append(&self, stream: &mut RlpStream) { self.stream_rlp(stream); }
}
impl Decodable for BlockHeader {
fn decode(r: &Rlp) -> Result<Self, DecoderError> {
let rlp_size = r.as_raw().len();
let mut rlp_part = BlockHeaderRlpPart {
parent_hash: r.val_at(0)?,
height: r.val_at(1)?,
timestamp: r.val_at(2)?,
author: r.val_at(3)?,
transactions_root: r.val_at(4)?,
deferred_state_root: r.val_at(5)?,
deferred_receipts_root: r.val_at(6)?,
deferred_logs_bloom_hash: r.val_at(7)?,
blame: r.val_at(8)?,
difficulty: r.val_at(9)?,
adaptive: r.val_at::<u8>(10)? == 1,
gas_limit: r.val_at(11)?,
referee_hashes: r.list_at(12)?,
custom: vec![],
nonce: r.val_at(13)?,
pos_reference: r.val_at(14).unwrap_or(None),
base_price: r.val_at(15).unwrap_or(None),
};
for i in (14
+ rlp_part.pos_reference.is_some() as usize
+ rlp_part.base_price.is_some() as usize)
..r.item_count()?
{
if rlp_part.height
>= *CIP112_TRANSITION_HEIGHT.get().expect("initialized")
{
rlp_part.custom.push(r.val_at(i)?);
} else {
rlp_part.custom.push(r.at(i)?.as_raw().to_vec());
}
}
let mut header = BlockHeader {
rlp_part,
hash: None,
pow_hash: None,
approximated_rlp_size: rlp_size,
};
header.compute_hash();
Ok(header)
}
}
#[derive(Clone, Copy, Debug, Eq, RlpDecodable, RlpEncodable, PartialEq)]
pub struct BasePrice {
pub core_base_price: U256,
pub espace_base_price: U256,
}
#[cfg(test)]
mod tests {
use super::BlockHeaderBuilder;
use crate::{
hash::keccak,
receipt::{BlockReceipts, Receipt},
TransactionStatus,
};
use cfx_types::{Bloom, KECCAK_EMPTY_BLOOM, U256};
use std::{str::FromStr, sync::Arc};
#[test]
fn test_logs_bloom_hash_no_receipts() {
let receipts = vec![]; let hash = BlockHeaderBuilder::compute_block_logs_bloom_hash(&receipts);
assert_eq!(hash, KECCAK_EMPTY_BLOOM);
let receipts = (1..11)
.map(|_| {
Arc::new(BlockReceipts {
receipts: vec![],
block_number: 0,
secondary_reward: U256::zero(),
tx_execution_error_messages: vec![],
})
})
.collect(); let hash = BlockHeaderBuilder::compute_block_logs_bloom_hash(&receipts);
assert_eq!(hash, KECCAK_EMPTY_BLOOM);
}
#[test]
fn test_logs_bloom_hash_empty_receipts() {
let receipt = Receipt {
accumulated_gas_used: U256::zero(),
gas_fee: U256::zero(),
gas_sponsor_paid: false,
logs: vec![],
outcome_status: TransactionStatus::Success,
log_bloom: Bloom::zero(),
storage_sponsor_paid: false,
storage_collateralized: vec![],
storage_released: vec![],
burnt_gas_fee: None,
};
let receipts = (1..11)
.map(|_| {
Arc::new(BlockReceipts {
receipts: (1..11).map(|_| receipt.clone()).collect(),
block_number: 0,
secondary_reward: U256::zero(),
tx_execution_error_messages: vec!["".into(); 10],
})
})
.collect();
let hash = BlockHeaderBuilder::compute_block_logs_bloom_hash(&receipts);
assert_eq!(hash, KECCAK_EMPTY_BLOOM);
}
#[test]
fn test_logs_bloom_hash() {
let block1 = BlockReceipts {
receipts: vec![
Receipt {
accumulated_gas_used: 0.into(),
gas_fee: 0.into(),
gas_sponsor_paid: false,
logs: vec![],
outcome_status: TransactionStatus::Success,
log_bloom: Bloom::from_str(
"11111111111111111111111111111111\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000",
)
.unwrap(),
storage_sponsor_paid: false,
storage_collateralized: vec![],
storage_released: vec![],
burnt_gas_fee: None,
},
Receipt {
accumulated_gas_used: U256::zero(),
gas_fee: U256::zero(),
gas_sponsor_paid: false,
logs: vec![],
outcome_status: TransactionStatus::Success,
log_bloom: Bloom::from_str(
"00000000000000000000000000000000\
22222222222222222222222222222222\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000",
)
.unwrap(),
storage_sponsor_paid: false,
storage_collateralized: vec![],
storage_released: vec![],
burnt_gas_fee: None,
},
],
block_number: 0,
secondary_reward: U256::zero(),
tx_execution_error_messages: vec!["".into(); 2],
};
let block2 = BlockReceipts {
receipts: vec![Receipt {
accumulated_gas_used: U256::zero(),
gas_fee: U256::zero(),
gas_sponsor_paid: false,
logs: vec![],
outcome_status: TransactionStatus::Success,
log_bloom: Bloom::from_str(
"44444444444444440000000000000000\
44444444444444440000000000000000\
44444444444444440000000000000000\
44444444444444440000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000",
)
.unwrap(),
storage_sponsor_paid: false,
storage_collateralized: vec![],
storage_released: vec![],
burnt_gas_fee: None,
}],
block_number: 0,
secondary_reward: U256::zero(),
tx_execution_error_messages: vec!["".into()],
};
let expected = keccak(
"55555555555555551111111111111111\
66666666666666662222222222222222\
44444444444444440000000000000000\
44444444444444440000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000\
00000000000000000000000000000000"
.parse::<Bloom>()
.unwrap(),
);
let receipts = vec![Arc::new(block1), Arc::new(block2)];
let hash = BlockHeaderBuilder::compute_block_logs_bloom_hash(&receipts);
assert_eq!(hash, expected);
}
}