From 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 23 Feb 2018 22:07:34 +0100 Subject: [PATCH] Get rid of RefCell in header. --- ethcore/src/block.rs | 163 ++++++++------ ethcore/src/client/test_client.rs | 17 +- ethcore/src/engines/authority_round/mod.rs | 2 +- ethcore/src/engines/tendermint/mod.rs | 2 +- ethcore/src/ethereum/ethash.rs | 2 +- ethcore/src/header.rs | 248 +++++++++++++-------- ethcore/src/machine.rs | 16 +- ethcore/src/miner/miner.rs | 4 +- ethcore/src/snapshot/block.rs | 4 +- ethcore/src/spec/spec.rs | 3 +- machine/src/lib.rs | 1 + 11 files changed, 269 insertions(+), 193 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index fc0d607f3f0..0bcd8fffb61 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -31,7 +31,7 @@ use vm::{EnvInfo, LastHashes}; use engines::EthEngine; use error::{Error, BlockError}; use factory::Factories; -use header::{Header, Seal}; +use header::{Header, HeaderMut, Seal}; use receipt::{Receipt, TransactionOutcome}; use state::State; use state_db::StateDB; @@ -318,18 +318,18 @@ impl<'x> OpenBlock<'x> { engine: engine, }; - r.block.header.set_parent_hash(parent.hash()); - r.block.header.set_number(number); - r.block.header.set_author(author); - r.block.header.set_timestamp_now(parent.timestamp()); - r.block.header.set_extra_data(extra_data); - r.block.header.note_dirty(); + r.block.header.alter(|header| { + header.set_parent_hash(parent.hash()); + header.set_number(number); + header.set_author(author); + header.set_timestamp_now(parent.timestamp()); + header.set_extra_data(extra_data); - let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); - let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); - - engine.machine().populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target); - engine.populate_from_parent(&mut r.block.header, parent); + let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); + let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); + engine.machine().populate_from_parent(header, parent, gas_floor_target, gas_ceil_target); + engine.populate_from_parent(header.mutable_header(), parent); + }); engine.machine().on_new_block(&mut r.block)?; engine.on_new_block(&mut r.block, is_epoch_begin)?; @@ -337,39 +337,14 @@ impl<'x> OpenBlock<'x> { Ok(r) } - /// Alter the author for the block. - pub fn set_author(&mut self, author: Address) { self.block.header.set_author(author); } - - /// Alter the timestamp of the block. - pub fn set_timestamp(&mut self, timestamp: u64) { self.block.header.set_timestamp(timestamp); } - - /// Alter the difficulty for the block. - pub fn set_difficulty(&mut self, a: U256) { self.block.header.set_difficulty(a); } - - /// Alter the gas limit for the block. - pub fn set_gas_limit(&mut self, a: U256) { self.block.header.set_gas_limit(a); } - - /// Alter the gas limit for the block. - pub fn set_gas_used(&mut self, a: U256) { self.block.header.set_gas_used(a); } - - /// Alter the uncles hash the block. - pub fn set_uncles_hash(&mut self, h: H256) { self.block.header.set_uncles_hash(h); } - - /// Alter transactions root for the block. - pub fn set_transactions_root(&mut self, h: H256) { self.block.header.set_transactions_root(h); } - - /// Alter the receipts root for the block. - pub fn set_receipts_root(&mut self, h: H256) { self.block.header.set_receipts_root(h); } - - /// Alter the extra_data for the block. - pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> { - if extra_data.len() > self.engine.maximum_extra_data_size() { - Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()})) - } else { - self.block.header.set_extra_data(extra_data); - Ok(()) - } + /// Alter header parameters and recompute hash. + pub fn alter_header(&mut self, f: F) { + self.block.header.alter(f) } + // + // /// Alter the extra_data for the block. + // pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> { + // } /// Add an uncle to the block, if possible. /// @@ -450,13 +425,26 @@ impl<'x> OpenBlock<'x> { /// Populate self from a header. pub fn populate_from(&mut self, header: &Header) { - self.set_difficulty(*header.difficulty()); - self.set_gas_limit(*header.gas_limit()); - self.set_timestamp(header.timestamp()); - self.set_author(header.author().clone()); - self.set_extra_data(header.extra_data().clone()).unwrap_or_else(|e| warn!("Couldn't set extradata: {}. Ignoring.", e)); - self.set_uncles_hash(header.uncles_hash().clone()); - self.set_transactions_root(header.transactions_root().clone()); + let extra_data = if header.extra_data().len() > self.engine.maximum_extra_data_size() { + let e = BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: header.extra_data().len()}); + warn!("Couldn't set extradata: {}. Ignoring.", e); + None + } else { + Some(header.extra_data().clone()) + }; + + self.alter_header(|h| { + h.set_difficulty(*header.difficulty()); + h.set_gas_limit(*header.gas_limit()); + h.set_timestamp(header.timestamp()); + h.set_author(header.author().clone()); + h.set_uncles_hash(header.uncles_hash().clone()); + h.set_transactions_root(header.transactions_root().clone()); + + if let Some(extra_data) = extra_data { + h.set_extra_data(extra_data); + } + }); } /// Turn this into a `ClosedBlock`. @@ -472,13 +460,22 @@ impl<'x> OpenBlock<'x> { if let Err(e) = s.block.state.commit() { warn!("Encountered error on state commit: {}", e); } - s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); + let transactions_root = ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes())); let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out(); - s.block.header.set_uncles_hash(keccak(&uncle_bytes)); - s.block.header.set_state_root(s.block.state.root().clone()); - s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))); - s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator - s.block.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used)); + let uncle_hash = keccak(&uncle_bytes); + let state_root = *s.block.state.root(); + let receipts_root = ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes())); + let log_bloom = s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator + let gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used); + + s.block.header.alter(|header| { + header.set_transactions_root(transactions_root); + header.set_uncles_hash(uncle_hash); + header.set_state_root(state_root); + header.set_receipts_root(receipts_root); + header.set_log_bloom(log_bloom); + header.set_gas_used(gas_used); + }); ClosedBlock { block: s.block, @@ -498,20 +495,39 @@ impl<'x> OpenBlock<'x> { if let Err(e) = s.block.state.commit() { warn!("Encountered error on state commit: {}", e); } - if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &KECCAK_NULL_RLP { - s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); - } - let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out(); - if s.block.header.uncles_hash().is_zero() || s.block.header.uncles_hash() == &KECCAK_EMPTY_LIST_RLP { - s.block.header.set_uncles_hash(keccak(&uncle_bytes)); - } - if s.block.header.receipts_root().is_zero() || s.block.header.receipts_root() == &KECCAK_NULL_RLP { - s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))); - } - s.block.header.set_state_root(s.block.state.root().clone()); - s.block.header.set_log_bloom(s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator - s.block.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used)); + let transactions_root = if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &KECCAK_NULL_RLP { + Some(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))) + } else { None }; + + let receipts_root = if s.block.header.receipts_root().is_zero() || s.block.header.receipts_root() == &KECCAK_NULL_RLP { + Some(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))) + } else { None }; + + let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| { s.append_raw(&u.rlp(Seal::With), 1); s }).out(); + let uncles_hash =if s.block.header.uncles_hash().is_zero() || s.block.header.uncles_hash() == &KECCAK_EMPTY_LIST_RLP { + Some(keccak(&uncle_bytes)) + } else { None }; + + let state_root = *s.block.state.root(); + let log_bloom = s.block.receipts.iter().fold(Bloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator + let gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used); + + s.block.header.alter(|header| { + if let Some(transactions_root) = transactions_root { + header.set_transactions_root(transactions_root); + } + if let Some(uncles_hash) = uncles_hash { + header.set_uncles_hash(uncles_hash); + } + if let Some(receipts_root) = receipts_root { + header.set_receipts_root(receipts_root); + } + + header.set_state_root(state_root); + header.set_log_bloom(log_bloom); + header.set_gas_used(gas_used); + }); LockedBlock { block: s.block, @@ -574,7 +590,7 @@ impl LockedBlock { return Err(BlockError::InvalidSealArity( Mismatch { expected: expected_seal_fields, found: seal.len() })); } - s.block.header.set_seal(seal); + s.block.header.alter(|h| h.set_seal(seal)); Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }) } @@ -587,7 +603,7 @@ impl LockedBlock { seal: Vec, ) -> Result { let mut s = self; - s.block.header.set_seal(seal); + s.block.header.alter(|h| h.set_seal(seal)); // TODO: passing state context to avoid engines owning it? match engine.verify_local_seal(&s.block.header) { @@ -602,7 +618,8 @@ impl LockedBlock { for receipt in &mut block.block.receipts { receipt.outcome = TransactionOutcome::Unknown; } - block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes()))); + let receipts_root = ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes())); + block.block.header.alter(|h| h.set_receipts_root(receipts_root)); block } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 9746ff95416..5b5696433b1 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -233,7 +233,7 @@ impl TestBlockChainClient { pub fn add_blocks(&self, count: usize, with: EachBlockWith) { let len = self.numbers.read().len(); for n in len..(len + count) { - let mut header = BlockHeader::new(); + let mut header = BlockHeader::new().unlock(); header.set_difficulty(From::from(n)); header.set_parent_hash(self.last_hash.read().clone()); header.set_number(n as BlockNumber); @@ -242,11 +242,11 @@ impl TestBlockChainClient { let uncles = match with { EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => { let mut uncles = RlpStream::new_list(1); - let mut uncle_header = BlockHeader::new(); + let mut uncle_header = BlockHeader::new().unlock(); uncle_header.set_difficulty(From::from(n)); uncle_header.set_parent_hash(self.last_hash.read().clone()); uncle_header.set_number(n as BlockNumber); - uncles.append(&uncle_header); + uncles.append(&uncle_header.lock()); header.set_uncles_hash(keccak(uncles.as_raw())); uncles }, @@ -273,6 +273,7 @@ impl TestBlockChainClient { _ => ::rlp::EMPTY_LIST_RLP.to_vec() }; + let header = header.lock(); let mut rlp = RlpStream::new_list(3); rlp.append(&header); rlp.append_raw(&txs, 1); @@ -284,8 +285,10 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid extra data. pub fn corrupt_block(&self, n: BlockNumber) { let hash = self.block_hash(BlockId::Number(n)).unwrap(); - let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let mut header = header.unlock(); header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); + let header = header.lock(); let mut rlp = RlpStream::new_list(3); rlp.append(&header); rlp.append_raw(&::rlp::NULL_RLP, 1); @@ -296,8 +299,10 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid parent hash. pub fn corrupt_block_parent(&self, n: BlockNumber) { let hash = self.block_hash(BlockId::Number(n)).unwrap(); - let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode(); + let mut header = header.unlock(); header.set_parent_hash(H256::from(42)); + let header = header.lock(); let mut rlp = RlpStream::new_list(3); rlp.append(&header); rlp.append_raw(&::rlp::NULL_RLP, 1); @@ -387,7 +392,7 @@ impl MiningBlockChainClient for TestBlockChainClient { false, ).expect("Opening block for tests will not fail."); // TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks) - open_block.set_timestamp(*self.latest_block_timestamp.read()); + open_block.alter_header(|h| h.set_timestamp(*self.latest_block_timestamp.read())); open_block } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 54b9c14a8aa..749d4382fe8 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -787,7 +787,7 @@ impl Engine for AuthorityRound { }; let score = calculate_score(parent_step.into(), current_step.into(), current_empty_steps_len.into()); - header.set_difficulty(score); + header.alter(|h| h.set_difficulty(score)); } fn seals_internally(&self) -> Option { diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index b408d1eee7a..1351678190a 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -466,7 +466,7 @@ impl Engine for Tendermint { + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into(); - header.set_difficulty(new_difficulty); + header.alter(|h| h.set_difficulty(new_difficulty)); } /// Should this node participate. diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index a9ee79f304d..2e566685fda 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -187,7 +187,7 @@ impl Engine for Arc { fn populate_from_parent(&self, header: &mut Header, parent: &Header) { let difficulty = self.calculate_difficulty(header, parent); - header.set_difficulty(difficulty); + header.alter(|h| h.set_difficulty(difficulty)); } fn on_new_block( diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index cfac04f3ca1..2768878e686 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -17,7 +17,6 @@ //! Block header. use std::cmp; -use std::cell::RefCell; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP, keccak}; use heapsize::HeapSizeOf; use ethereum_types::{H256, U256, Address, Bloom}; @@ -75,28 +74,19 @@ pub struct Header { /// Vector of post-RLP-encoded fields. seal: Vec, + /// The memoized RLP representation of this header *including* the seal fields. + rlp: Bytes, /// The memoized hash of the RLP representation *including* the seal fields. - hash: RefCell>, + hash: H256, + /// The memoized RLP representation of this header *without* the seal fields. + bare_rlp: Bytes, /// The memoized hash of the RLP representation *without* the seal fields. - bare_hash: RefCell>, + bare_hash: H256, } impl PartialEq for Header { fn eq(&self, c: &Header) -> bool { - self.parent_hash == c.parent_hash && - self.timestamp == c.timestamp && - self.number == c.number && - self.author == c.author && - self.transactions_root == c.transactions_root && - self.uncles_hash == c.uncles_hash && - self.extra_data == c.extra_data && - self.state_root == c.state_root && - self.receipts_root == c.receipts_root && - self.log_bloom == c.log_bloom && - self.gas_used == c.gas_used && - self.gas_limit == c.gas_limit && - self.difficulty == c.difficulty && - self.seal == c.seal + self.hash == c.hash } } @@ -120,9 +110,11 @@ impl Default for Header { difficulty: U256::default(), seal: vec![], - hash: RefCell::new(None), - bare_hash: RefCell::new(None), - } + rlp: vec![], + hash: H256::default(), + bare_rlp: vec![], + bare_hash: H256::default(), + }.recompute_hashes() } } @@ -143,8 +135,6 @@ impl Header { /// Get the extra data field of the header. pub fn extra_data(&self) -> &Bytes { &self.extra_data } - /// Get a mutable reference to extra_data - pub fn extra_data_mut(&mut self) -> &mut Bytes { self.note_dirty(); &mut self.extra_data } /// Get the state root field of the header. pub fn state_root(&self) -> &H256 { &self.state_root } @@ -172,74 +162,6 @@ impl Header { }).collect() } - // TODO: seal_at, set_seal_at &c. - - /// Set the number field of the header. - pub fn set_parent_hash(&mut self, a: H256) { self.parent_hash = a; self.note_dirty(); } - /// Set the uncles hash field of the header. - pub fn set_uncles_hash(&mut self, a: H256) { self.uncles_hash = a; self.note_dirty(); } - /// Set the state root field of the header. - pub fn set_state_root(&mut self, a: H256) { self.state_root = a; self.note_dirty(); } - /// Set the transactions root field of the header. - pub fn set_transactions_root(&mut self, a: H256) { self.transactions_root = a; self.note_dirty() } - /// Set the receipts root field of the header. - pub fn set_receipts_root(&mut self, a: H256) { self.receipts_root = a; self.note_dirty() } - /// Set the log bloom field of the header. - pub fn set_log_bloom(&mut self, a: Bloom) { self.log_bloom = a; self.note_dirty() } - /// Set the timestamp field of the header. - pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); } - /// Set the timestamp field of the header to the current time. - pub fn set_timestamp_now(&mut self, but_later_than: u64) { self.timestamp = cmp::max(get_time().sec as u64, but_later_than + 1); self.note_dirty(); } - /// Set the number field of the header. - pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); } - /// Set the author field of the header. - pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } } - - /// Set the extra data field of the header. - pub fn set_extra_data(&mut self, a: Bytes) { if a != self.extra_data { self.extra_data = a; self.note_dirty(); } } - - /// Set the gas used field of the header. - pub fn set_gas_used(&mut self, a: U256) { self.gas_used = a; self.note_dirty(); } - /// Set the gas limit field of the header. - pub fn set_gas_limit(&mut self, a: U256) { self.gas_limit = a; self.note_dirty(); } - - /// Set the difficulty field of the header. - pub fn set_difficulty(&mut self, a: U256) { self.difficulty = a; self.note_dirty(); } - /// Set the seal field of the header. - pub fn set_seal(&mut self, a: Vec) { self.seal = a; self.note_dirty(); } - - /// Get the hash of this header (keccak of the RLP). - pub fn hash(&self) -> H256 { - let mut hash = self.hash.borrow_mut(); - match &mut *hash { - &mut Some(ref h) => h.clone(), - hash @ &mut None => { - let h = self.rlp_keccak(Seal::With); - *hash = Some(h.clone()); - h - } - } - } - - /// Get the hash of the header excluding the seal - pub fn bare_hash(&self) -> H256 { - let mut hash = self.bare_hash.borrow_mut(); - match &mut *hash { - &mut Some(ref h) => h.clone(), - hash @ &mut None => { - let h = self.rlp_keccak(Seal::Without); - *hash = Some(h.clone()); - h - } - } - } - - /// Note that some fields have changed. Resets the memoised hash. - pub fn note_dirty(&self) { - *self.hash.borrow_mut() = None; - *self.bare_hash.borrow_mut() = None; - } - // TODO: make these functions traity /// Place this header into an RLP stream `s`, optionally `with_seal`. pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { @@ -266,18 +188,142 @@ impl Header { /// Get the RLP of this header, optionally `with_seal`. pub fn rlp(&self, with_seal: Seal) -> Bytes { - let mut s = RlpStream::new(); - self.stream_rlp(&mut s, with_seal); - s.out() + match with_seal { + Seal::With => self.rlp.clone(), + Seal::Without => self.bare_rlp.clone(), + } } /// Get the SHA3 (Keccak) of this header, optionally `with_seal`. - pub fn rlp_keccak(&self, with_seal: Seal) -> H256 { keccak(self.rlp(with_seal)) } + pub fn rlp_keccak(&self, with_seal: Seal) -> H256 { + match with_seal { + Seal::With => self.hash, + Seal::Without => self.bare_hash, + } + } /// Encode the header, getting a type-safe wrapper around the RLP. pub fn encoded(&self) -> ::encoded::Header { ::encoded::Header::new(self.rlp(Seal::With)) } + + /// Get the hash of this header (keccak of the RLP). + pub fn hash(&self) -> H256 { + self.hash + } + + /// Get the hash of the header excluding the seal + pub fn bare_hash(&self) -> H256 { + self.bare_hash + } + + pub fn alter(&mut self, f: F) { + use std::ptr; + + let mut old_t = unsafe { ptr::read(self) }.unlock(); + f(&mut old_t); + unsafe { ptr::write(self, old_t.lock()) }; + } + + pub fn unlock(self) -> HeaderMut { + HeaderMut { + header: self, + is_dirty: false, + } + } + + fn recompute_hashes(mut self) -> Self { + Self::recompute(&mut self); + self + } + + fn recompute(header: &mut Self) { + let (rlp, bare_rlp) = { + let rlp = |with_seal| { + let mut s = RlpStream::new(); + header.stream_rlp(&mut s, with_seal); + s.out() + }; + (rlp(Seal::Without), rlp(Seal::With)) + }; + + header.rlp = rlp; + header.bare_rlp = bare_rlp; + + header.hash = keccak(&header.rlp); + header.bare_hash = keccak(&header.bare_rlp); + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HeaderMut { + header: Header, + is_dirty: bool, +} + +impl HeaderMut { + /// Lock this header and recompute hash. + pub fn lock(mut self) -> Header { + if self.is_dirty { + self.header.recompute_hashes() + } else { + self.header + } + } + + /// Get a mutable reference to header. + /// + /// NOTE It's not safe to clone such header! RLP/Hash values are most likely invalid. + pub fn mutable_header(&mut self) -> &mut Header { + self.note_dirty(); + &mut self.header + } + /// Get the number field of the header. + pub fn number(&self) -> BlockNumber { self.header.number } + /// Get the gas used field of the header. + pub fn gas_used(&self) -> &U256 { &self.header.gas_used } + + /// Set the number field of the header. + pub fn set_parent_hash(&mut self, a: H256) { self.header.parent_hash = a; self.note_dirty(); } + /// Set the uncles hash field of the header. + pub fn set_uncles_hash(&mut self, a: H256) { self.header.uncles_hash = a; self.note_dirty(); } + /// Set the state root field of the header. + pub fn set_state_root(&mut self, a: H256) { self.header.state_root = a; self.note_dirty(); } + /// Set the transactions root field of the header. + pub fn set_transactions_root(&mut self, a: H256) { self.header.transactions_root = a; self.note_dirty() } + /// Set the receipts root field of the header. + pub fn set_receipts_root(&mut self, a: H256) { self.header.receipts_root = a; self.note_dirty() } + /// Set the log bloom field of the header. + pub fn set_log_bloom(&mut self, a: Bloom) { self.header.log_bloom = a; self.note_dirty() } + /// Set the timestamp field of the header. + pub fn set_timestamp(&mut self, a: u64) { self.header.timestamp = a; self.note_dirty(); } + /// Set the timestamp field of the header to the current time. + pub fn set_timestamp_now(&mut self, but_later_than: u64) { self.header.timestamp = cmp::max(get_time().sec as u64, but_later_than + 1); self.note_dirty(); } + /// Set the number field of the header. + pub fn set_number(&mut self, a: BlockNumber) { self.header.number = a; self.note_dirty(); } + /// Set the author field of the header. + pub fn set_author(&mut self, a: Address) { if a != self.header.author { self.header.author = a; self.note_dirty(); } } + + /// Set the extra data field of the header. + pub fn set_extra_data(&mut self, a: Bytes) { if a != self.header.extra_data { self.header.extra_data = a; self.note_dirty(); } } + + /// Get a mutable reference to extra_data + pub fn extra_data_mut(&mut self) -> &mut Bytes { self.note_dirty(); &mut self.header.extra_data } + + /// Set the gas used field of the header. + pub fn set_gas_used(&mut self, a: U256) { self.header.gas_used = a; self.note_dirty(); } + /// Set the gas limit field of the header. + pub fn set_gas_limit(&mut self, a: U256) { self.header.gas_limit = a; self.note_dirty(); } + + /// Set the difficulty field of the header. + pub fn set_difficulty(&mut self, a: U256) { self.header.difficulty = a; self.note_dirty(); } + /// Set the seal field of the header. + pub fn set_seal(&mut self, a: Vec) { self.header.seal = a; self.note_dirty(); } + + /// Note that some fields have changed. Resets the memoised hash. + pub fn note_dirty(&mut self) { + self.is_dirty = true; + } } impl Decodable for Header { @@ -297,15 +343,17 @@ impl Decodable for Header { timestamp: cmp::min(r.val_at::(11)?, u64::max_value().into()).as_u64(), extra_data: r.val_at(12)?, seal: vec![], - hash: RefCell::new(Some(keccak(r.as_raw()))), - bare_hash: RefCell::new(None), + rlp: vec![], + bare_rlp: vec![], + hash: Default::default(), + bare_hash: Default::default(), }; for i in 13..r.item_count()? { blockheader.seal.push(r.at(i)?.as_raw().to_vec()) } - Ok(blockheader) + Ok(blockheader.recompute_hashes()) } } @@ -335,7 +383,11 @@ impl ::parity_machine::Header for Header { impl ::parity_machine::ScoredHeader for Header { fn score(&self) -> &U256 { self.difficulty() } - fn set_score(&mut self, score: U256) { self.set_difficulty(score) } + + fn set_score(&mut self, score: U256) { + self.difficulty = score; + Header::recompute(self) + } } #[cfg(test)] diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index e302a461340..66b949382b7 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -25,7 +25,7 @@ use builtin::Builtin; use client::BlockChainClient; use error::Error; use executive::Executive; -use header::{BlockNumber, Header}; +use header::{BlockNumber, Header, HeaderMut}; use spec::CommonParams; use state::{CleanupMode, Substate}; use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType}; @@ -201,7 +201,7 @@ impl EthereumMachine { /// Populate a header's fields based on its parent's header. /// Usually implements the chain scoring rule based on weight. /// The gas floor target must not be lower than the engine's minimum gas limit. - pub fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { + pub fn populate_from_parent(&self, header: &mut HeaderMut, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) { header.set_difficulty(parent.difficulty().clone()); if let Some(ref ethash_params) = self.ethash_extensions { @@ -495,7 +495,7 @@ mod tests { ); let mut parent = ::header::Header::new(); - let mut header = ::header::Header::new(); + let mut header = ::header::Header::new().unlock(); header.set_number(1); // this test will work for this constant only @@ -504,25 +504,25 @@ mod tests { // when parent.gas_limit < gas_floor_target: parent.set_gas_limit(U256::from(50_000)); machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(50_024)); + assert_eq!(*header.clone().lock().gas_limit(), U256::from(50_024)); // when parent.gas_limit > gas_ceil_target: parent.set_gas_limit(U256::from(250_000)); machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(249_787)); + assert_eq!(*header.clone().lock().gas_limit(), U256::from(249_787)); // when parent.gas_limit is in miner's range header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); - assert_eq!(*header.gas_limit(), U256::from(150_035)); + assert_eq!(*header.clone().lock().gas_limit(), U256::from(150_035)); // when parent.gas_limit is in miner's range // && we can NOT increase it to be multiple of constant header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); machine.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(149_998)); + assert_eq!(*header.clone().lock().gas_limit(), U256::from(149_998)); // when parent.gas_limit is in miner's range // && we can NOT increase it to be multiple of constant @@ -530,6 +530,6 @@ mod tests { header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); machine.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); - assert_eq!(*header.gas_limit(), U256::from(150_002)); + assert_eq!(*header.clone().lock().gas_limit(), U256::from(150_002)); } } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 7fffe5511ef..759ce3b2941 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -360,7 +360,7 @@ impl Miner { }; if self.options.infinite_pending_block { - open_block.set_gas_limit(!U256::zero()); + open_block.alter_header(|h| h.set_gas_limit(!U256::zero())); } (open_block, last_work_hash) @@ -696,7 +696,7 @@ impl MinerService for Miner { fn set_author(&self, address: Address, password: Option) -> Result<(), AccountError> { self.params.write().author = address; - if self.engine.seals_internally().is_some() { + if self.engine.seals_internally().is_some() && password.is_some() { if let Some(ref ap) = self.accounts { let password = password.unwrap_or_default(); // Sign test message diff --git a/ethcore/src/snapshot/block.rs b/ethcore/src/snapshot/block.rs index 98215b3244d..44047bb6add 100644 --- a/ethcore/src/snapshot/block.rs +++ b/ethcore/src/snapshot/block.rs @@ -91,7 +91,7 @@ impl AbridgedBlock { pub fn to_block(&self, parent_hash: H256, number: u64, receipts_root: H256) -> Result { let rlp = UntrustedRlp::new(&self.rlp); - let mut header: Header = Default::default(); + let mut header = Header::default().unlock(); header.set_parent_hash(parent_hash); header.set_author(rlp.val_at(0)?); header.set_state_root(rlp.val_at(1)?); @@ -124,7 +124,7 @@ impl AbridgedBlock { header.set_seal(seal_fields); Ok(Block { - header: header, + header: header.lock(), transactions: transactions, uncles: uncles, }) diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index ba9e578db6d..5de648031a7 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -572,7 +572,7 @@ impl Spec { /// Get the header of the genesis block. pub fn genesis_header(&self) -> Header { - let mut header: Header = Default::default(); + let mut header = Header::default().unlock(); header.set_parent_hash(self.parent_hash.clone()); header.set_timestamp(self.timestamp); header.set_number(0); @@ -590,6 +590,7 @@ impl Spec { let r = Rlp::new(&self.seal_rlp); r.iter().map(|f| f.as_raw().to_vec()).collect() }); + let header = header.lock(); trace!(target: "spec", "Header hash is {}", header.hash()); header } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3a45c38d2ef..c06b0904d76 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -77,6 +77,7 @@ pub trait Transactions: LiveBlock { pub trait Machine: for<'a> LocalizedMachine<'a> { /// The block header type. type Header: Header; + /// The live block type. type LiveBlock: LiveBlock; /// A handle to a blockchain client for this machine.