From a134f939e9ed92dffb53ecc749ca965bc116f050 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 22 Mar 2016 13:05:18 +0100 Subject: [PATCH] Non-functioning draft of code. --- ethcore/src/block.rs | 14 ++++- ethcore/src/state.rs | 12 ++++ miner/src/miner.rs | 135 +++++++++++++++++++++++++++++++--------- rpc/src/v1/impls/eth.rs | 10 ++- 4 files changed, 134 insertions(+), 37 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d700b854f..bddf09182 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -75,7 +75,7 @@ impl Decodable for Block { } /// Internal type for a block's common elements. -#[derive(Debug)] +#[derive(Clone)] pub struct ExecutedBlock { base: Block, @@ -168,9 +168,11 @@ pub struct OpenBlock<'x> { /// and collected the uncles. /// /// There is no function available to push a transaction. +#[derive(Clone)] pub struct ClosedBlock { block: ExecutedBlock, uncle_bytes: Bytes, + last_hashes: LastHashes, } /// A block that has a valid seal. @@ -290,6 +292,7 @@ impl<'x> OpenBlock<'x> { ClosedBlock { block: s.block, uncle_bytes: uncle_bytes, + last_hashes: s.last_hashes, } } } @@ -332,6 +335,15 @@ impl ClosedBlock { /// Drop this object and return the underlieing database. pub fn drain(self) -> Box { self.block.state.drop().1 } + + /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. + pub fn reopen(self, engine: &Engine) -> OpenBlock { + OpenBlock { + block: self.block, + engine: engine, + last_hashes: self.last_hashes, + } + } } impl SealedBlock { diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 78084e6db..1f6ca7df4 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -339,6 +339,18 @@ impl fmt::Debug for State { } } +impl Clone for State { + fn clone(&self) -> State { + State { + db: self.db.spawn(), + root: self.root.clone(), + cache: RefCell::new(self.cache.borrow().clone()), + snapshots: RefCell::new(self.snapshots.borrow().clone()), + account_start_nonce: self.account_start_nonce.clone(), + } + } +} + #[cfg(test)] mod tests { diff --git a/miner/src/miner.rs b/miner/src/miner.rs index e1b314d57..55ef1f6c4 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -28,6 +28,72 @@ use ethcore::error::{Error}; use ethcore::transaction::SignedTransaction; use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails}; +struct SealingWork { + /// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this. + would_seal: Option, + /// Currently being sealed by miners. + being_sealed: Vec, +} + +impl SealingWork { + // inspect the work that would be given. + fn pending_ref(&self) -> Option<&ClosedBlock> { + self.would_seal.as_ref().or(self.being_sealed.last().as_ref()) + } + + // return the a reference to forst block that returns true to `f`. + fn find_if(&self, f: F) -> Option<&ClosedBlock> where F: Fn(&ClosedBlock) -> bool { + if would_seal.as_ref().map(&f).unwrap_or(false) { + would_seal.as_ref() + } else { + being_sealed.iter().find_if(f) + } + } + + // used for getting the work to be done. + fn use_pending_ref(&mut self) -> Option<&ClosedBlock> { + if let Some(x) = self.would_seal.take() { + self.being_sealed.push(x); + if self.being_sealed.len() > MAX_SEALING_BLOCKS_CACHE { + self.being_sealed.erase(0); + } + } + self.being_sealed.last().as_ref() + } + + // set new work to be done. + fn set_pending(&mut self, b: ClosedBlock) { + self.would_seal = Some(b); + } + + // get the pending block if `f(pending)`. if there is no pending block or it doesn't pass `f`, None. + // will not destroy a block if a reference to it has previously been returned by `use_pending_ref`. + fn pending_if(&self, f: F) -> Option where F: Fn(&ClosedBlock) -> bool { + // a bit clumsy - TODO: think about a nicer way of expressing this. + if let Some(x) = self.would_seal.take() { + if f(&x) { + Some(x) + } else { + self.would_seal = x; + None + } + } else { + being_sealed.last().as_ref().filter(&b).map(|b| b.clone()) +/* being_sealed.last().as_ref().and_then(|b| if f(b) { + Some(b.clone()) + } else { + None + })*/ + } + } + + // clears everything. + fn reset(&mut self) { + self.would_seal = None; + self.being_sealed.clear(); + } +} + /// Keeps track of transactions using priority queue and holds currently mined block. pub struct Miner { transaction_queue: Mutex, @@ -35,20 +101,33 @@ pub struct Miner { // for sealing... sealing_enabled: AtomicBool, sealing_block_last_request: Mutex, - sealing_block: Mutex>, + sealing_work: Mutex, gas_floor_target: RwLock, author: RwLock
, extra_data: RwLock, } +/* + let sealing_work = self.sealing_work.lock(); + + // TODO: check to see if last ClosedBlock in would_seals is same. + // if so, duplicate, re-open and push any new transactions. + // if at least one was pushed successfully, close and enqueue new ClosedBlock; + // and remove first ClosedBlock from the queue.. + +*/ + impl Default for Miner { fn default() -> Miner { Miner { transaction_queue: Mutex::new(TransactionQueue::new()), sealing_enabled: AtomicBool::new(false), sealing_block_last_request: Mutex::new(0), - sealing_block: Mutex::new(None), + sealing_work: Mutex::new(SealingWork{ + would_seal: None, + being_sealed: vec![], + }), gas_floor_target: RwLock::new(U256::zero()), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), @@ -107,7 +186,7 @@ impl Miner { transactions, ); - *self.sealing_block.lock().unwrap() = b.map(|(block, invalid_transactions)| { + if let Some((block, invalid_transactions)) = b { let mut queue = self.transaction_queue.lock().unwrap(); queue.remove_all( &invalid_transactions.into_iter().collect::>(), @@ -116,8 +195,8 @@ impl Miner { balance: chain.balance(a), } ); - block - }); + self.sealing_work.lock().unwrap().set_pending(block); + } } fn update_gas_limit(&self, chain: &BlockChainClient) { @@ -138,11 +217,11 @@ impl MinerService for Miner { fn status(&self) -> MinerStatus { let status = self.transaction_queue.lock().unwrap().status(); - let block = self.sealing_block.lock().unwrap(); + let sealing_work = self.sealing_work.lock().unwrap(); MinerStatus { transactions_in_pending_queue: status.pending, transactions_in_future_queue: status.future, - transactions_in_pending_block: block.as_ref().map_or(0, |b| b.transactions().len()), + transactions_in_pending_block: block.pending_ref().map_or(0, |b| b.transactions().len()), } } @@ -167,40 +246,36 @@ impl MinerService for Miner { if should_disable_sealing { self.sealing_enabled.store(false, atomic::Ordering::Relaxed); - *self.sealing_block.lock().unwrap() = None; + *self.sealing_work.lock().unwrap().reset(); } else if self.sealing_enabled.load(atomic::Ordering::Relaxed) { self.prepare_sealing(chain); } } - fn sealing_block(&self, chain: &BlockChainClient) -> &Mutex> { - if self.sealing_block.lock().unwrap().is_none() { + fn map_sealing_work(&self, chain: &BlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { + let have_work = self.sealing_work.lock().unwrap().pending_ref().is_none(); + if !have_work { self.sealing_enabled.store(true, atomic::Ordering::Relaxed); - self.prepare_sealing(chain); } *self.sealing_block_last_request.lock().unwrap() = chain.chain_info().best_block_number; - &self.sealing_block + self.sealing_work.lock().unwrap().use_pending().map(f) } fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> { - let mut maybe_b = self.sealing_block.lock().unwrap(); - match *maybe_b { - Some(ref b) if b.hash() == pow_hash => {} - _ => { return Err(Error::PowHashInvalid); } - } - - let b = maybe_b.take(); - match chain.try_seal(b.unwrap(), seal) { - Err(old) => { - *maybe_b = Some(old); - Err(Error::PowInvalid) - } - Ok(sealed) => { - // TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice. - try!(chain.import_block(sealed.rlp_bytes())); - Ok(()) + if let Some(b) = self.sealing_work().lock().unwrap().take_if(|b| &b.hash() == &pow_hash) { + match chain.try_seal(b.unwrap(), seal) { + Err(old) => { + Err(Error::PowInvalid) + } + Ok(sealed) => { + // TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice. + try!(chain.import_block(sealed.rlp_bytes())); + Ok(()) + } } + } else { + Err(Error::PowHashInvalid) } } @@ -281,7 +356,7 @@ mod tests { let miner = Miner::default(); // when - let res = miner.sealing_block(&client); + let res = miner.would_seal(&client); // then assert!(res.lock().unwrap().is_some(), "Expected closed block"); @@ -292,7 +367,7 @@ mod tests { // given let client = TestBlockChainClient::default(); let miner = Miner::default(); - let res = miner.sealing_block(&client); + let res = miner.would_seal(&client); // TODO [ToDr] Uncomment after fixing TestBlockChainClient // assert!(res.lock().unwrap().is_some(), "Expected closed block"); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index d7ee478bf..0c6b4c63d 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -334,7 +334,7 @@ impl Eth for EthClient match params { Params::None => { let client = take_weak!(self.client); - // check if we're still syncing and return empty strings int that case + // check if we're still syncing and return empty strings in that case { let sync = take_weak!(self.sync); if sync.status().state != SyncState::Idle && client.queue_info().is_empty() { @@ -343,17 +343,15 @@ impl Eth for EthClient } let miner = take_weak!(self.miner); - let client = take_weak!(self.client); - let u = miner.sealing_block(client.deref()).lock().unwrap(); - match *u { - Some(ref b) => { + miner.map_sealing_work(client.deref(), |b| match b { + Some(b) => { let pow_hash = b.hash(); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); let seed_hash = Ethash::get_seedhash(b.block().header().number()); to_value(&(pow_hash, seed_hash, target)) } _ => Err(Error::internal_error()) - } + }) }, _ => Err(Error::invalid_params()) }